@neocompose/cli 0.73.2 → 0.75.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
@@ -368,27 +368,27 @@ var init_member_subtree_distribution = __esm({
368
368
  }
369
369
  });
370
370
 
371
- // node_modules/uuid/dist-node/regex.js
371
+ // ../node_modules/uuid/dist-node/regex.js
372
372
  var regex_default;
373
373
  var init_regex = __esm({
374
- "node_modules/uuid/dist-node/regex.js"() {
374
+ "../node_modules/uuid/dist-node/regex.js"() {
375
375
  regex_default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/i;
376
376
  }
377
377
  });
378
378
 
379
- // node_modules/uuid/dist-node/validate.js
379
+ // ../node_modules/uuid/dist-node/validate.js
380
380
  function validate(uuid) {
381
381
  return typeof uuid === "string" && regex_default.test(uuid);
382
382
  }
383
383
  var validate_default;
384
384
  var init_validate = __esm({
385
- "node_modules/uuid/dist-node/validate.js"() {
385
+ "../node_modules/uuid/dist-node/validate.js"() {
386
386
  init_regex();
387
387
  validate_default = validate;
388
388
  }
389
389
  });
390
390
 
391
- // node_modules/uuid/dist-node/parse.js
391
+ // ../node_modules/uuid/dist-node/parse.js
392
392
  function parse(uuid) {
393
393
  if (!validate_default(uuid)) {
394
394
  throw TypeError("Invalid UUID");
@@ -398,19 +398,19 @@ function parse(uuid) {
398
398
  }
399
399
  var parse_default;
400
400
  var init_parse = __esm({
401
- "node_modules/uuid/dist-node/parse.js"() {
401
+ "../node_modules/uuid/dist-node/parse.js"() {
402
402
  init_validate();
403
403
  parse_default = parse;
404
404
  }
405
405
  });
406
406
 
407
- // node_modules/uuid/dist-node/stringify.js
407
+ // ../node_modules/uuid/dist-node/stringify.js
408
408
  function unsafeStringify(arr, offset = 0) {
409
409
  return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
410
410
  }
411
411
  var byteToHex;
412
412
  var init_stringify = __esm({
413
- "node_modules/uuid/dist-node/stringify.js"() {
413
+ "../node_modules/uuid/dist-node/stringify.js"() {
414
414
  byteToHex = [];
415
415
  for (let i2 = 0; i2 < 256; ++i2) {
416
416
  byteToHex.push((i2 + 256).toString(16).slice(1));
@@ -418,18 +418,18 @@ var init_stringify = __esm({
418
418
  }
419
419
  });
420
420
 
421
- // node_modules/uuid/dist-node/rng.js
421
+ // ../node_modules/uuid/dist-node/rng.js
422
422
  function rng() {
423
423
  return crypto.getRandomValues(rnds8);
424
424
  }
425
425
  var rnds8;
426
426
  var init_rng = __esm({
427
- "node_modules/uuid/dist-node/rng.js"() {
427
+ "../node_modules/uuid/dist-node/rng.js"() {
428
428
  rnds8 = new Uint8Array(16);
429
429
  }
430
430
  });
431
431
 
432
- // node_modules/uuid/dist-node/v35.js
432
+ // ../node_modules/uuid/dist-node/v35.js
433
433
  function stringToBytes(str) {
434
434
  str = unescape(encodeURIComponent(str));
435
435
  const bytes = new Uint8Array(str.length);
@@ -467,7 +467,7 @@ function v35(version, hash, value, namespace, buf, offset) {
467
467
  }
468
468
  var DNS, URL2;
469
469
  var init_v35 = __esm({
470
- "node_modules/uuid/dist-node/v35.js"() {
470
+ "../node_modules/uuid/dist-node/v35.js"() {
471
471
  init_parse();
472
472
  init_stringify();
473
473
  DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
@@ -475,7 +475,7 @@ var init_v35 = __esm({
475
475
  }
476
476
  });
477
477
 
478
- // node_modules/uuid/dist-node/v4.js
478
+ // ../node_modules/uuid/dist-node/v4.js
479
479
  function v4(options, buf, offset) {
480
480
  if (!buf && !options && crypto.randomUUID) {
481
481
  return crypto.randomUUID();
@@ -504,14 +504,14 @@ function _v4(options, buf, offset) {
504
504
  }
505
505
  var v4_default;
506
506
  var init_v4 = __esm({
507
- "node_modules/uuid/dist-node/v4.js"() {
507
+ "../node_modules/uuid/dist-node/v4.js"() {
508
508
  init_rng();
509
509
  init_stringify();
510
510
  v4_default = v4;
511
511
  }
512
512
  });
513
513
 
514
- // node_modules/uuid/dist-node/sha1.js
514
+ // ../node_modules/uuid/dist-node/sha1.js
515
515
  import { createHash } from "node:crypto";
516
516
  function sha1(bytes) {
517
517
  if (Array.isArray(bytes)) {
@@ -523,18 +523,18 @@ function sha1(bytes) {
523
523
  }
524
524
  var sha1_default;
525
525
  var init_sha1 = __esm({
526
- "node_modules/uuid/dist-node/sha1.js"() {
526
+ "../node_modules/uuid/dist-node/sha1.js"() {
527
527
  sha1_default = sha1;
528
528
  }
529
529
  });
530
530
 
531
- // node_modules/uuid/dist-node/v5.js
531
+ // ../node_modules/uuid/dist-node/v5.js
532
532
  function v5(value, namespace, buf, offset) {
533
533
  return v35(80, sha1_default, value, namespace, buf, offset);
534
534
  }
535
535
  var v5_default;
536
536
  var init_v5 = __esm({
537
- "node_modules/uuid/dist-node/v5.js"() {
537
+ "../node_modules/uuid/dist-node/v5.js"() {
538
538
  init_sha1();
539
539
  init_v35();
540
540
  v5.DNS = DNS;
@@ -543,9 +543,9 @@ var init_v5 = __esm({
543
543
  }
544
544
  });
545
545
 
546
- // node_modules/uuid/dist-node/index.js
546
+ // ../node_modules/uuid/dist-node/index.js
547
547
  var init_dist_node = __esm({
548
- "node_modules/uuid/dist-node/index.js"() {
548
+ "../node_modules/uuid/dist-node/index.js"() {
549
549
  init_parse();
550
550
  init_v4();
551
551
  init_v5();
@@ -3150,24 +3150,30 @@ function isNSInstructionTry(value) {
3150
3150
  }
3151
3151
  return true;
3152
3152
  }
3153
- function isNSInstructionChangeListener(value) {
3154
- const instruction = value;
3155
- if (instruction?.type !== "addChangeListener" /* addChangeListener */ && instruction?.type !== "removeChangeListener" /* removeChangeListener */)
3156
- return false;
3157
- const target = instruction.target;
3153
+ function isNSChangeListenerTarget(value) {
3154
+ const target = value;
3158
3155
  if (!target || typeof target.memberId !== "string" || target.memberId.length === 0)
3159
3156
  return false;
3160
3157
  if (!isNSPointer(target.owner) || !isNSTypeInfo(target.typeInfo))
3161
3158
  return false;
3162
- if (!Object.values(NSWritability).includes(target.writability)) return false;
3163
- return isNSPointer(instruction.listener);
3159
+ return target.writability !== void 0 && Object.values(NSWritability).includes(target.writability);
3160
+ }
3161
+ function isNSInstructionChangeListener(value) {
3162
+ const instruction = value;
3163
+ if (instruction?.type !== "addChangeListener" /* addChangeListener */ && instruction?.type !== "removeChangeListener" /* removeChangeListener */)
3164
+ return false;
3165
+ return isNSChangeListenerTarget(instruction.target) && isNSPointer(instruction.listener);
3166
+ }
3167
+ function isNSInstructionClearChangeListeners(value) {
3168
+ const instruction = value;
3169
+ return instruction?.type === "clearChangeListeners" /* clearChangeListeners */ && isNSChangeListenerTarget(instruction.target);
3164
3170
  }
3165
3171
  function isNSInstructionDebug(value) {
3166
3172
  const v = value;
3167
3173
  return v?.type === "debug" /* debug */ && ["log", "warning", "error", "assert"].includes(v.severity ?? "") && isNSPointer(v.message) && isNSTypeInfo(v.messageType) && (v.severity === "assert" ? isNSPointer(v.condition) : v.condition === void 0);
3168
3174
  }
3169
3175
  function isNSInstruction(value) {
3170
- return isNSInstructionDebug(value) || isNSInstructionVariable(value) || isNSInstructionIfBranch(value) || isNSInstructionReturn(value) || isNSInstructionThrow(value) || isNSInstructionAssign(value) || isNSInstructionCollectionCall(value) || isNSInstructionFunctionCall(value) || isNSInstructionConditionLoop(value) || isNSInstructionFor(value) || isNSInstructionForEach(value) || isNSInstructionBreak(value) || isNSInstructionContinue(value) || isNSInstructionSwitch(value) || isNSInstructionTry(value) || isNSInstructionChangeListener(value) || isNSInstructionAddActionListener(value) || isNSInstructionRemoveActionListener(value);
3176
+ return isNSInstructionDebug(value) || isNSInstructionVariable(value) || isNSInstructionIfBranch(value) || isNSInstructionReturn(value) || isNSInstructionThrow(value) || isNSInstructionAssign(value) || isNSInstructionCollectionCall(value) || isNSInstructionFunctionCall(value) || isNSInstructionConditionLoop(value) || isNSInstructionFor(value) || isNSInstructionForEach(value) || isNSInstructionBreak(value) || isNSInstructionContinue(value) || isNSInstructionSwitch(value) || isNSInstructionTry(value) || isNSInstructionChangeListener(value) || isNSInstructionClearChangeListeners(value) || isNSInstructionAddActionListener(value) || isNSInstructionRemoveActionListener(value);
3171
3177
  }
3172
3178
  function isNSInstructions(value) {
3173
3179
  return Array.isArray(value) && value.every(isNSInstruction);
@@ -28491,6 +28497,15 @@ function inspectListenerAnnotation(annotation2, parameters) {
28491
28497
  return;
28492
28498
  }
28493
28499
  const parameter4 = parameters.byName.get(name);
28500
+ if (!parameter4 && parameters.getters.has(name)) {
28501
+ errors.push(
28502
+ new CompileError(
28503
+ `'${name}' is a getter. Getter listeners can't be authored. Subscribe with '+=' from a function.`,
28504
+ expression.pos
28505
+ )
28506
+ );
28507
+ return;
28508
+ }
28494
28509
  if (!parameter4) {
28495
28510
  errors.push(
28496
28511
  new CompileError(
@@ -28534,7 +28549,10 @@ function createListenerAnnotationProvider(project, context = {}) {
28534
28549
  if (cached) return cached;
28535
28550
  const parameters = [];
28536
28551
  const byName = /* @__PURE__ */ new Map();
28552
+ const getters = /* @__PURE__ */ new Set();
28537
28553
  for (const symbol of declaration.members) {
28554
+ if (symbol.kind === "property" && symbol.computed === true && symbol.static !== true)
28555
+ getters.add(symbol.name);
28538
28556
  if (!isChangeListenerSymbol(symbol)) continue;
28539
28557
  if (memberAccessFailure({ project, context, owner: declaration, symbol }))
28540
28558
  continue;
@@ -28558,7 +28576,8 @@ function createListenerAnnotationProvider(project, context = {}) {
28558
28576
  const result = {
28559
28577
  namedOnly: true,
28560
28578
  parameters,
28561
- byName
28579
+ byName,
28580
+ getters
28562
28581
  };
28563
28582
  cache.set(key, result);
28564
28583
  return result;
@@ -30799,7 +30818,7 @@ function intersectCollectionWritability(left, right) {
30799
30818
  const writable = (value) => value === "local" /* Local */ || value === "save" /* Save */ || value === "session" /* Session */ || value === "runtime" /* Runtime */;
30800
30819
  return writable(left) && writable(right) ? "runtime" /* Runtime */ : "readOnly" /* ReadOnly */;
30801
30820
  }
30802
- var CONSTRUCTING_FUNCTION_KINDS, VARIANT_INITIALIZE_NAME, VARIANT_APPLY_NAME, MATH_NAMESPACE_NAME, MATH_FUNCTIONS, MATH_CONSTANTS, LIST_STATIC_FUNCTIONS, WRITE_TARGET_DESCRIPTIONS, FALLTHROUGH_LABELS, Scope, UnresolvedPersistedListenerReference, StrictNeoScriptResolver, SPRITE_DERIVED_MEMBERS;
30821
+ var CONSTRUCTING_FUNCTION_KINDS, VARIANT_INITIALIZE_NAME, VARIANT_APPLY_NAME, MATH_NAMESPACE_NAME, MATH_FUNCTIONS, MATH_CONSTANTS, LIST_STATIC_FUNCTIONS, WRITE_TARGET_DESCRIPTIONS, ON_CHANGED_OPERATIONS_ERROR, FALLTHROUGH_LABELS, Scope, UnresolvedPersistedListenerReference, StrictNeoScriptResolver, SPRITE_DERIVED_MEMBERS;
30803
30822
  var init_strict_resolver = __esm({
30804
30823
  "../packages/neoscript-language/src/strict-resolver.ts"() {
30805
30824
  "use strict";
@@ -30860,6 +30879,7 @@ var init_strict_resolver = __esm({
30860
30879
  projectValue: "a stored project value",
30861
30880
  unknown: "something other than `this` or a local"
30862
30881
  };
30882
+ ON_CHANGED_OPERATIONS_ERROR = "OnChanged supports only '+=', '-=', and Clear().";
30863
30883
  FALLTHROUGH_LABELS = {
30864
30884
  function: "NSFunction",
30865
30885
  getter: "getter",
@@ -32202,6 +32222,12 @@ var init_strict_resolver = __esm({
32202
32222
  this.resolveExpression(statement.expr, scope);
32203
32223
  const debug = this.resolveDebugStatement(statement.expr, scope);
32204
32224
  if (debug) return debug;
32225
+ const listenerClear = this.resolveChangeListenerClear(
32226
+ statement.expr,
32227
+ scope,
32228
+ statement.pos
32229
+ );
32230
+ if (listenerClear) return listenerClear;
32205
32231
  const actionClear = this.resolveActionClear(
32206
32232
  statement.expr,
32207
32233
  scope,
@@ -32562,8 +32588,20 @@ var init_strict_resolver = __esm({
32562
32588
  const observed = this.resolveExpression(statement.target.receiver, scope);
32563
32589
  const observedType = neoScriptMemberLookupType(observed.type);
32564
32590
  const declared = observedType.kind === "named" && this.project.typeById.get(observedType.typeId)?.members.some((member) => member.name === "OnChanged");
32565
- if (!declared)
32566
- return this.resolveChangeSubscription(statement, observed, scope);
32591
+ if (!declared) {
32592
+ if (statement.op !== "+=" && statement.op !== "-=")
32593
+ throw new CompileError(ON_CHANGED_OPERATIONS_ERROR, statement.pos);
32594
+ return this.resolveChangeListener(
32595
+ statement.target,
32596
+ observed,
32597
+ scope,
32598
+ statement.pos,
32599
+ {
32600
+ kind: statement.op === "+=" ? "addChangeListener" /* AddChangeListener */ : "removeChangeListener" /* RemoveChangeListener */,
32601
+ handler: statement.value
32602
+ }
32603
+ );
32604
+ }
32567
32605
  }
32568
32606
  const local = statement.target.kind === "ident" ? scope.lookup(statement.target.name) : null;
32569
32607
  if (local?.readonlyBinding) {
@@ -32747,112 +32785,180 @@ var init_strict_resolver = __esm({
32747
32785
  pos
32748
32786
  );
32749
32787
  }
32750
- resolveChangeSubscription(statement, observed, scope) {
32751
- if (statement.op !== "+=" && statement.op !== "-=") {
32752
- throw new CompileError(
32753
- "OnChanged supports only '+=' and '-=' subscriptions.",
32754
- statement.pos
32755
- );
32756
- }
32757
- if (statement.target.kind !== "member") {
32788
+ /** `X.M.OnChanged.Clear()` empties M's listener set on X (P70, P106). */
32789
+ resolveChangeListenerClear(expression, scope, pos) {
32790
+ if (expression.kind !== "call" || expression.callee.kind !== "member" || expression.callee.name !== "Clear" || expression.callee.receiver.kind !== "member" || expression.callee.receiver.name !== "OnChanged")
32791
+ return null;
32792
+ const onChanged = expression.callee.receiver;
32793
+ const observed = this.resolveExpression(onChanged.receiver, scope);
32794
+ const observedType = neoScriptMemberLookupType(observed.type);
32795
+ if (observedType.kind === "named" && this.project.typeById.get(observedType.typeId)?.members.some((member) => member.name === "OnChanged"))
32796
+ return null;
32797
+ if (expression.callee.optional)
32758
32798
  throw new CompileError(
32759
- "OnChanged requires a declared member slot.",
32760
- statement.pos
32799
+ "OnChanged is never null, so optional-chained Clear is not supported.",
32800
+ pos
32761
32801
  );
32762
- }
32763
- if (statement.target.optional) {
32802
+ requireArgCount("Clear", expression.args, 0, pos);
32803
+ return this.resolveChangeListener(onChanged, observed, scope, pos, {
32804
+ kind: "clearChangeListeners" /* ClearChangeListeners */
32805
+ });
32806
+ }
32807
+ resolveChangeListener(onChanged, observed, scope, pos, edit) {
32808
+ if (onChanged.optional) {
32764
32809
  throw new CompileError(
32765
32810
  "OnChanged cannot use an optional subscription target.",
32766
- statement.pos
32811
+ pos
32767
32812
  );
32768
32813
  }
32769
32814
  if (observed.optionalChainType !== void 0) {
32770
32815
  throw new CompileError(
32771
32816
  "OnChanged cannot subscribe through optional chaining; narrow the receiver first.",
32772
- statement.pos
32817
+ pos
32773
32818
  );
32774
32819
  }
32775
32820
  const member = observed.symbol;
32776
32821
  if (!member)
32777
32822
  throw new CompileError(
32778
32823
  "OnChanged requires a declared member, not a local variable.",
32779
- statement.pos
32824
+ pos
32780
32825
  );
32781
- if (observed.pointer.type !== "keyOf" /* KeyOf */)
32826
+ if (observed.pointer.type === "staticMember" /* StaticMember */ || observed.pointer.type === "callGetter" /* CallGetter */ && observed.pointer.receiver.kind === "static") {
32827
+ const access = onChanged.receiver;
32828
+ const name = access.kind === "member" && access.receiver.kind === "ident" ? `${access.receiver.name}.${member.name}` : member.name;
32782
32829
  throw new CompileError(
32783
- "OnChanged requires a stored instance member, not a static member.",
32784
- statement.pos
32830
+ `OnChanged requires an instance member. '${name}' is static.`,
32831
+ pos
32832
+ );
32833
+ }
32834
+ if (observed.pointer.type === "callGetter" /* CallGetter */)
32835
+ return this.resolveGetterChangeListener(
32836
+ onChanged.receiver,
32837
+ observed,
32838
+ observed.pointer,
32839
+ member,
32840
+ scope,
32841
+ pos,
32842
+ edit
32785
32843
  );
32844
+ if (observed.pointer.type !== "keyOf" /* KeyOf */)
32845
+ throw new CompileError("OnChanged requires a declared member slot.", pos);
32786
32846
  if (!observed.pointer.memberId)
32787
32847
  throw new CompileError(
32788
32848
  "OnChanged cannot observe a collection entry.",
32789
- statement.pos
32849
+ pos
32790
32850
  );
32791
32851
  if (member.computed) {
32792
32852
  throw new CompileError(
32793
32853
  "OnChanged cannot observe a computed member.",
32794
- statement.pos
32854
+ pos
32795
32855
  );
32796
32856
  }
32797
32857
  if (member.readonlyDeclaration) {
32798
32858
  throw new CompileError(
32799
32859
  "OnChanged cannot observe a readonly declaration.",
32800
- statement.pos
32860
+ pos
32801
32861
  );
32802
32862
  }
32803
32863
  if (observed.type.kind === "delegate" || observed.type.kind === "action") {
32804
32864
  throw new CompileError(
32805
32865
  "OnChanged cannot observe a callable member.",
32806
- statement.pos
32866
+ pos
32807
32867
  );
32808
32868
  }
32809
32869
  const writability = observed.userData ? "runtime" /* Runtime */ : observed.writability;
32810
32870
  if (writability !== "save" /* Save */ && writability !== "session" /* Session */ && writability !== "runtime" /* Runtime */ && writability !== "local" /* Local */) {
32811
32871
  throw new CompileError(
32812
32872
  "OnChanged requires a member whose storage is writable at gameplay runtime.",
32813
- statement.pos
32873
+ pos
32814
32874
  );
32815
32875
  }
32816
32876
  this.validateConstructorWriteTarget(
32817
32877
  observed.pointer,
32818
- "subscribe to",
32819
- statement.pos,
32878
+ edit.kind === "clearChangeListeners" /* ClearChangeListeners */ ? "clear listeners on" : "subscribe to",
32879
+ pos,
32820
32880
  scope,
32821
32881
  false
32822
32882
  );
32823
- if (!statement.value) {
32883
+ const storageType = this.assignmentStorageType(
32884
+ onChanged.receiver,
32885
+ observed,
32886
+ null,
32887
+ scope
32888
+ );
32889
+ return this.changeListenerInstruction(
32890
+ edit,
32891
+ {
32892
+ owner: observed.pointer.keyOf.pointer,
32893
+ memberId: member.canonicalMemberId ?? observed.pointer.memberId,
32894
+ typeInfo: toWireType(storageType, this.project),
32895
+ writability
32896
+ },
32897
+ storageType,
32898
+ scope,
32899
+ pos
32900
+ );
32901
+ }
32902
+ /**
32903
+ * P106. A getter subscription lives only in the running client's memory:
32904
+ * nothing persists it, so construction replay would silently drop one.
32905
+ */
32906
+ resolveGetterChangeListener(getterRead, observed, pointer, member, scope, pos, edit) {
32907
+ if (pointer.dispatch === "base") {
32824
32908
  throw new CompileError(
32825
- "OnChanged subscription requires a handler member.",
32826
- statement.pos
32909
+ `OnChanged can't observe a base getter. Subscribe to '${member.name}' on the instance.`,
32910
+ pos
32827
32911
  );
32828
32912
  }
32829
- const storageType = this.assignmentStorageType(
32830
- statement.target.receiver,
32913
+ if (pointer.receiver.kind !== "instance") {
32914
+ throw new CompileError(
32915
+ `OnChanged requires an instance receiver for getter '${member.name}'.`,
32916
+ pos
32917
+ );
32918
+ }
32919
+ const kind = this.context.kind;
32920
+ if (isNeoScriptConstructionDocumentKind(kind) || kind === "migration") {
32921
+ throw new CompileError(
32922
+ edit.kind === "clearChangeListeners" /* ClearChangeListeners */ ? `A getter subscription lives only for the play session, so ${dialogueBodyKindLabel(kind)} can't clear one. Clear from a function.` : `A getter subscription lives only for the play session, so ${dialogueBodyKindLabel(kind)} can't make one. Subscribe from a function.`,
32923
+ pos
32924
+ );
32925
+ }
32926
+ const getterType = this.assignmentStorageType(
32927
+ getterRead,
32831
32928
  observed,
32832
32929
  null,
32833
32930
  scope
32834
32931
  );
32835
- const expected = {
32836
- kind: "action",
32837
- parameterTypes: [storageType]
32838
- };
32932
+ return this.changeListenerInstruction(
32933
+ edit,
32934
+ {
32935
+ owner: pointer.receiver.pointer,
32936
+ memberId: member.canonicalMemberId ?? pointer.memberId,
32937
+ typeInfo: toWireType(getterType, this.project),
32938
+ writability: "session" /* Session */
32939
+ },
32940
+ getterType,
32941
+ scope,
32942
+ pos
32943
+ );
32944
+ }
32945
+ changeListenerInstruction(edit, target, observedType, scope, pos) {
32946
+ if (edit.kind === "clearChangeListeners" /* ClearChangeListeners */)
32947
+ return { type: edit.kind, target };
32948
+ if (!edit.handler) {
32949
+ throw new CompileError(
32950
+ "OnChanged subscription requires a handler member.",
32951
+ pos
32952
+ );
32953
+ }
32839
32954
  const listener = this.resolveActionListener(
32840
- statement.value,
32841
- expected,
32955
+ edit.handler,
32956
+ { kind: "action", parameterTypes: [observedType] },
32842
32957
  scope,
32843
32958
  true,
32844
32959
  true
32845
32960
  );
32846
- return {
32847
- type: statement.op === "+=" ? "addChangeListener" /* AddChangeListener */ : "removeChangeListener" /* RemoveChangeListener */,
32848
- target: {
32849
- owner: observed.pointer.keyOf.pointer,
32850
- memberId: member.canonicalMemberId ?? observed.pointer.memberId,
32851
- typeInfo: toWireType(storageType, this.project),
32852
- writability
32853
- },
32854
- listener
32855
- };
32961
+ return { type: edit.kind, target, listener };
32856
32962
  }
32857
32963
  /**
32858
32964
  * P62 §3.2. `+=` / `-=` on an NSAction member are subscription operators,
@@ -34970,6 +35076,8 @@ var init_strict_resolver = __esm({
34970
35076
  entryUserData
34971
35077
  };
34972
35078
  }
35079
+ if (name === "OnChanged")
35080
+ throw new CompileError(ON_CHANGED_OPERATIONS_ERROR, pos);
34973
35081
  throw new CompileError(
34974
35082
  `Cannot read member '${name}' on ${this.describe(receiver.type)}`,
34975
35083
  pos
@@ -51689,7 +51797,7 @@ var init_registry = __esm({
51689
51797
  "../packages/manifest/schema-contract/registry.mjs"() {
51690
51798
  "use strict";
51691
51799
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
51692
- cliVersion: "0.73.2",
51800
+ cliVersion: "0.75.0",
51693
51801
  projectFileUploadBatchSize: 32,
51694
51802
  documentRecords: {
51695
51803
  member: {
@@ -54292,7 +54400,7 @@ var init_document_contracts2 = __esm({
54292
54400
  }
54293
54401
  });
54294
54402
 
54295
- // node_modules/valibot/dist/index.mjs
54403
+ // ../node_modules/valibot/dist/index.mjs
54296
54404
  // @__NO_SIDE_EFFECTS__
54297
54405
  function getGlobalConfig(config$1) {
54298
54406
  if (!config$1 && !store$4) return DEFAULT_CONFIG;
@@ -55038,11 +55146,6 @@ function variant(key, options, message$1) {
55038
55146
  }
55039
55147
  });
55040
55148
  }
55041
- function parse2(schema, input, config$1) {
55042
- const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig(config$1));
55043
- if (dataset.issues) throw new ValiError(dataset.issues);
55044
- return dataset.value;
55045
- }
55046
55149
  // @__NO_SIDE_EFFECTS__
55047
55150
  function pipe(...pipe$1) {
55048
55151
  return _standardSchema({
@@ -55070,27 +55173,15 @@ function safeParse(schema, input, config$1) {
55070
55173
  issues: dataset.issues
55071
55174
  };
55072
55175
  }
55073
- var store$4, DEFAULT_CONFIG, store$3, store$2, store$1, ValiError, EMOJI_REGEX, ABORT_EARLY_CONFIG;
55176
+ var store$4, DEFAULT_CONFIG, store$3, store$2, store$1, EMOJI_REGEX, ABORT_EARLY_CONFIG;
55074
55177
  var init_dist = __esm({
55075
- "node_modules/valibot/dist/index.mjs"() {
55178
+ "../node_modules/valibot/dist/index.mjs"() {
55076
55179
  DEFAULT_CONFIG = {
55077
55180
  lang: void 0,
55078
55181
  message: void 0,
55079
55182
  abortEarly: void 0,
55080
55183
  abortPipeEarly: void 0
55081
55184
  };
55082
- ValiError = class extends Error {
55083
- /**
55084
- * Creates a Valibot error with useful information.
55085
- *
55086
- * @param issues The error issues.
55087
- */
55088
- constructor(issues) {
55089
- super(issues[0].message);
55090
- this.name = "ValiError";
55091
- this.issues = issues;
55092
- }
55093
- };
55094
55185
  EMOJI_REGEX = new RegExp("^(?:[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation}))*)+$", "u");
55095
55186
  ABORT_EARLY_CONFIG = { abortEarly: true };
55096
55187
  }
@@ -55890,6 +55981,47 @@ var init_schema_member = __esm({
55890
55981
  }
55891
55982
  });
55892
55983
 
55984
+ // node_modules/valibot/dist/index.mjs
55985
+ // @__NO_SIDE_EFFECTS__
55986
+ function getGlobalConfig2(config$1) {
55987
+ if (!config$1 && !store$42) return DEFAULT_CONFIG2;
55988
+ return {
55989
+ lang: config$1?.lang ?? store$42?.lang,
55990
+ message: config$1?.message,
55991
+ abortEarly: config$1?.abortEarly ?? store$42?.abortEarly,
55992
+ abortPipeEarly: config$1?.abortPipeEarly ?? store$42?.abortPipeEarly
55993
+ };
55994
+ }
55995
+ function parse2(schema, input, config$1) {
55996
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig2(config$1));
55997
+ if (dataset.issues) throw new ValiError(dataset.issues);
55998
+ return dataset.value;
55999
+ }
56000
+ var store$42, DEFAULT_CONFIG2, ValiError, EMOJI_REGEX2;
56001
+ var init_dist2 = __esm({
56002
+ "node_modules/valibot/dist/index.mjs"() {
56003
+ DEFAULT_CONFIG2 = {
56004
+ lang: void 0,
56005
+ message: void 0,
56006
+ abortEarly: void 0,
56007
+ abortPipeEarly: void 0
56008
+ };
56009
+ ValiError = class extends Error {
56010
+ /**
56011
+ * Creates a Valibot error with useful information.
56012
+ *
56013
+ * @param issues The error issues.
56014
+ */
56015
+ constructor(issues) {
56016
+ super(issues[0].message);
56017
+ this.name = "ValiError";
56018
+ this.issues = issues;
56019
+ }
56020
+ };
56021
+ EMOJI_REGEX2 = new RegExp("^(?:[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|(?![\\p{Emoji_Modifier_Base}\\u{1F1E6}-\\u{1F1FF}])\\p{Emoji_Presentation}))*)+$", "u");
56022
+ }
56023
+ });
56024
+
55893
56025
  // src/project-manifest/record-adapters/codecs.ts
55894
56026
  function inferMemberOwners(classes, members) {
55895
56027
  const owners = /* @__PURE__ */ new Map();
@@ -57385,7 +57517,7 @@ var init_codecs = __esm({
57385
57517
  "use strict";
57386
57518
  init_document_contracts2();
57387
57519
  init_schema_member();
57388
- init_dist();
57520
+ init_dist2();
57389
57521
  init_docs_text2();
57390
57522
  MEMBER_KIND = {
57391
57523
  Null: 0,
@@ -78870,6 +79002,9 @@ function listenerWritesByOwner(state) {
78870
79002
  }
78871
79003
  return index.owners;
78872
79004
  }
79005
+ function changeListenerWriteEdit(write) {
79006
+ return write.operation === "clear" ? { kind: "replace", targets: [] } : { kind: write.operation, target: write.listener };
79007
+ }
78873
79008
  var indexes, empty;
78874
79009
  var init_listener_write_index = __esm({
78875
79010
  "../src/runtime/listener-write-index.ts"() {
@@ -79618,6 +79753,151 @@ var init_world_grid_query = __esm({
79618
79753
  }
79619
79754
  });
79620
79755
 
79756
+ // ../src/runtime/neoscript/getter-subscriptions.ts
79757
+ function* readKeys(reads) {
79758
+ yield* reads.valueIds;
79759
+ yield* reads.containerIds;
79760
+ yield* reads.containerEntryReadIds;
79761
+ for (const id2 of reads.staticMemberIds) yield staticReadKey(id2);
79762
+ }
79763
+ var NeoScriptGetterSubscription, NO_SUBSCRIPTIONS, staticReadKey, NeoScriptGetterSubscriptions;
79764
+ var init_getter_subscriptions = __esm({
79765
+ "../src/runtime/neoscript/getter-subscriptions.ts"() {
79766
+ "use strict";
79767
+ init_initializer_context();
79768
+ NeoScriptGetterSubscription = class {
79769
+ constructor(rowId, getterId, classId) {
79770
+ this.rowId = rowId;
79771
+ this.getterId = getterId;
79772
+ this.classId = classId;
79773
+ }
79774
+ rowId;
79775
+ getterId;
79776
+ classId;
79777
+ targets = [];
79778
+ reads = new InitializerReadSet();
79779
+ queued = false;
79780
+ /** Queued only to arm: its first read calls no handler. */
79781
+ arming = true;
79782
+ /** Its handlers are running. */
79783
+ handling = false;
79784
+ };
79785
+ NO_SUBSCRIPTIONS = [];
79786
+ staticReadKey = (memberId) => `static:${memberId}`;
79787
+ NeoScriptGetterSubscriptions = class _NeoScriptGetterSubscriptions {
79788
+ byRow = /* @__PURE__ */ new Map();
79789
+ byRead = /* @__PURE__ */ new Map();
79790
+ /** Reads no row id names, such as corpus or world queries, hear every write. */
79791
+ readingEverything = /* @__PURE__ */ new Set();
79792
+ pending = [];
79793
+ get empty() {
79794
+ return this.byRow.size === 0;
79795
+ }
79796
+ get(rowId, getterId) {
79797
+ return this.byRow.get(rowId)?.get(getterId);
79798
+ }
79799
+ add(subscription) {
79800
+ this.register(subscription);
79801
+ this.queue(subscription);
79802
+ }
79803
+ drop(subscription) {
79804
+ const getters = this.byRow.get(subscription.rowId);
79805
+ if (getters?.get(subscription.getterId) !== subscription) return;
79806
+ getters.delete(subscription.getterId);
79807
+ if (getters.size === 0) this.byRow.delete(subscription.rowId);
79808
+ this.unindex(subscription);
79809
+ }
79810
+ noteValueWrite(rowId) {
79811
+ this.noteRead(rowId);
79812
+ }
79813
+ noteStaticBinding(memberId) {
79814
+ this.noteRead(staticReadKey(memberId));
79815
+ }
79816
+ /** Replace what the subscription's last read touched. */
79817
+ setReads(subscription, reads) {
79818
+ this.unindex(subscription);
79819
+ subscription.reads = reads;
79820
+ if (this.get(subscription.rowId, subscription.getterId) !== subscription)
79821
+ return;
79822
+ if (reads.corpusReadReasons.size > 0 || reads.worldQueries.size > 0)
79823
+ this.readingEverything.add(subscription);
79824
+ for (const key of readKeys(reads)) {
79825
+ let subscriptions = this.byRead.get(key);
79826
+ if (subscriptions === void 0) {
79827
+ subscriptions = /* @__PURE__ */ new Set();
79828
+ this.byRead.set(key, subscriptions);
79829
+ }
79830
+ subscriptions.add(subscription);
79831
+ }
79832
+ }
79833
+ takePending() {
79834
+ if (this.pending.length === 0) return NO_SUBSCRIPTIONS;
79835
+ const pending = this.pending;
79836
+ this.pending = [];
79837
+ return pending;
79838
+ }
79839
+ /** A throw skipped `drained` from `next` on. They stay queued, so none goes deaf. */
79840
+ requeue(drained, next) {
79841
+ for (let index = next; index < drained.length; index += 1)
79842
+ this.pending.push(drained[index]);
79843
+ }
79844
+ /** A copy for a fresh runtime over the same rows, armed exactly as these are. */
79845
+ clone() {
79846
+ const cloned = new _NeoScriptGetterSubscriptions();
79847
+ const copies = /* @__PURE__ */ new Map();
79848
+ for (const getters of this.byRow.values()) {
79849
+ for (const source of getters.values()) {
79850
+ const copy = new NeoScriptGetterSubscription(
79851
+ source.rowId,
79852
+ source.getterId,
79853
+ source.classId
79854
+ );
79855
+ copy.targets.push(...source.targets);
79856
+ copy.arming = source.arming;
79857
+ copies.set(source, copy);
79858
+ cloned.register(copy);
79859
+ cloned.setReads(copy, source.reads);
79860
+ }
79861
+ }
79862
+ for (const source of this.pending) {
79863
+ const copy = copies.get(source);
79864
+ if (copy !== void 0) cloned.queue(copy);
79865
+ }
79866
+ return cloned;
79867
+ }
79868
+ register(subscription) {
79869
+ let getters = this.byRow.get(subscription.rowId);
79870
+ if (getters === void 0) {
79871
+ getters = /* @__PURE__ */ new Map();
79872
+ this.byRow.set(subscription.rowId, getters);
79873
+ }
79874
+ getters.set(subscription.getterId, subscription);
79875
+ }
79876
+ queue(subscription) {
79877
+ if (subscription.queued) return;
79878
+ subscription.queued = true;
79879
+ this.pending.push(subscription);
79880
+ }
79881
+ noteRead(key) {
79882
+ const subscriptions = this.byRead.get(key);
79883
+ if (subscriptions !== void 0)
79884
+ for (const subscription of subscriptions) this.queue(subscription);
79885
+ for (const subscription of this.readingEverything)
79886
+ if (!subscription.handling) this.queue(subscription);
79887
+ }
79888
+ unindex(subscription) {
79889
+ this.readingEverything.delete(subscription);
79890
+ for (const key of readKeys(subscription.reads)) {
79891
+ const subscriptions = this.byRead.get(key);
79892
+ if (subscriptions === void 0) continue;
79893
+ subscriptions.delete(subscription);
79894
+ if (subscriptions.size === 0) this.byRead.delete(key);
79895
+ }
79896
+ }
79897
+ };
79898
+ }
79899
+ });
79900
+
79621
79901
  // ../src/models/decimal/decimal-math.ts
79622
79902
  function pow10(exponent) {
79623
79903
  return TEN ** BigInt(exponent);
@@ -80115,6 +80395,7 @@ function evalMemberById(vm, id2) {
80115
80395
  return vm.members.find((a) => a.id === id2) ?? null;
80116
80396
  }
80117
80397
  function evalValueById(ctx, id2, runtimeValues, overlayValues) {
80398
+ ctx.__getterReads?.add(id2);
80118
80399
  if (ctx.__removedValueIds?.has(id2)) {
80119
80400
  ctx.valueDependencies?.add(id2);
80120
80401
  return null;
@@ -80787,8 +81068,13 @@ function bindThisValue(ctx, thisValue) {
80787
81068
  const rowId = thisValue === ctx.thisValue ? ctx.thisValueRowId : trackedRowForValueReference(thisValue, ctx)?.id;
80788
81069
  return { ...ctx, thisValue, thisValueRowId: rowId };
80789
81070
  }
81071
+ function observeValueWrite(ctx, row) {
81072
+ ctx.beforeValueWrite?.(row);
81073
+ ctx.__getterSubscriptions?.noteValueWrite(row.id);
81074
+ }
80790
81075
  function assertStoredReplayMutableValue(value, ctx, label, key) {
80791
- if (ctx.storedConstructionReplay !== true && !ctx.assertValueWritable && !ctx.beforeValueWrite)
81076
+ const observesWrites = ctx.beforeValueWrite !== void 0 || ctx.__getterSubscriptions?.empty === false;
81077
+ if (ctx.storedConstructionReplay !== true && !ctx.assertValueWritable && !observesWrites)
80792
81078
  return;
80793
81079
  let rowId = findKnownRowIdByValueReference(value, ctx);
80794
81080
  if (rowId === null) return;
@@ -80807,7 +81093,7 @@ function assertStoredReplayMutableValue(value, ctx, label, key) {
80807
81093
  );
80808
81094
  if (ctx.__runtimeSessionValues?.has(rowId) !== true)
80809
81095
  ctx.assertValueWritable?.(rowId);
80810
- if (row) ctx.beforeValueWrite?.(row);
81096
+ if (row) observeValueWrite(ctx, row);
80811
81097
  if (ctx.__runtimeSessionValues?.has(rowId) === true) return;
80812
81098
  if (ctx.storedConstructionReplay !== true) return;
80813
81099
  throw new NSGetterRuntimeError(
@@ -81390,10 +81676,14 @@ function inspectNeoScriptValueReference(ctx, value, collection, index) {
81390
81676
  const row = readNeoScriptRuntimeRow(ctx, id2);
81391
81677
  return row === null ? null : { row, member: memberForValueRow(row, ctx) };
81392
81678
  }
81679
+ function getterSubscriptionsFor(ctx) {
81680
+ return ctx.getterDispatch === "afterEntryStatement" /* AfterEntryStatement */ ? new NeoScriptGetterSubscriptions() : void 0;
81681
+ }
81393
81682
  function withEvaluationRuntime(ctx, writes) {
81394
81683
  if (ctx.__runtimeSessionValues !== void 0 && ctx.__executionState !== void 0 && ctx.__valueOverlay !== void 0) {
81395
81684
  ctx.__unorderedListProjections ??= createUnorderedListProjectionCache();
81396
81685
  ctx.__runtimeClassOverrides ??= /* @__PURE__ */ new Map();
81686
+ ctx.__getterSubscriptions ??= getterSubscriptionsFor(ctx);
81397
81687
  return ctx;
81398
81688
  }
81399
81689
  const overlay = ctx.__valueOverlay ?? (ctx.storedConstructionReplay === true ? /* @__PURE__ */ new Map() : buildValueOverlay(ctx.vm));
@@ -81418,6 +81708,7 @@ function withEvaluationRuntime(ctx, writes) {
81418
81708
  trigger: remap(ctx.dialogueContext.trigger)
81419
81709
  },
81420
81710
  __runtimeSessionValues: runtimeSessionValues,
81711
+ __getterSubscriptions: ctx.__getterSubscriptions ?? getterSubscriptionsFor(ctx),
81421
81712
  __copiedChangeListeners: ctx.__copiedChangeListeners ?? /* @__PURE__ */ new Map(),
81422
81713
  __runtimeClassOverrides: ctx.__runtimeClassOverrides ?? /* @__PURE__ */ new Map(),
81423
81714
  __saveStaticBindings: ctx.__saveStaticBindings ?? staticBindingMap(ctx.saveStaticBindings),
@@ -81624,33 +81915,41 @@ function finalizeConstructorAllocations(ctx, returnValue, ownsInvocationState, r
81624
81915
  const listenerEdges = /* @__PURE__ */ new Map();
81625
81916
  for (const write of state.writes) {
81626
81917
  if (!retainListenerReceivers || write.kind !== "changeListener") continue;
81918
+ const slotKey = JSON.stringify([write.ownerId, write.memberId]);
81919
+ if (write.operation === "clear") {
81920
+ listenerEdges.delete(slotKey);
81921
+ continue;
81922
+ }
81627
81923
  const key = JSON.stringify([
81628
- write.ownerId,
81629
- write.memberId,
81630
81924
  write.listener.memberId,
81631
81925
  write.listener.valueId
81632
81926
  ]);
81633
- if (write.operation === "remove") listenerEdges.delete(key);
81634
- else listenerEdges.set(key, write);
81927
+ let edges = listenerEdges.get(slotKey);
81928
+ if (write.operation === "remove") edges?.delete(key);
81929
+ else {
81930
+ if (edges === void 0) listenerEdges.set(slotKey, edges = /* @__PURE__ */ new Map());
81931
+ edges.set(key, write);
81932
+ }
81635
81933
  }
81636
81934
  const externalListenerReceivers = /* @__PURE__ */ new Set();
81637
- for (const write of listenerEdges.values()) {
81638
- if (write.listener.valueId === null) continue;
81639
- const receiverGroup = groupByRowId.get(write.listener.valueId);
81640
- if (receiverGroup === void 0) continue;
81641
- const ownerGroup = groupByRowId.get(write.ownerId);
81642
- if (ownerGroup === void 0) {
81643
- if (rowForCleanup(write.ownerId) !== null)
81644
- externalListenerReceivers.add(receiverGroup);
81645
- } else {
81646
- let dependencies = state.constructorGroupDependencies.get(ownerGroup);
81647
- if (dependencies === void 0) {
81648
- dependencies = /* @__PURE__ */ new Set();
81649
- state.constructorGroupDependencies.set(ownerGroup, dependencies);
81935
+ for (const edges of listenerEdges.values())
81936
+ for (const write of edges.values()) {
81937
+ if (write.listener.valueId === null) continue;
81938
+ const receiverGroup = groupByRowId.get(write.listener.valueId);
81939
+ if (receiverGroup === void 0) continue;
81940
+ const ownerGroup = groupByRowId.get(write.ownerId);
81941
+ if (ownerGroup === void 0) {
81942
+ if (rowForCleanup(write.ownerId) !== null)
81943
+ externalListenerReceivers.add(receiverGroup);
81944
+ } else {
81945
+ let dependencies = state.constructorGroupDependencies.get(ownerGroup);
81946
+ if (dependencies === void 0) {
81947
+ dependencies = /* @__PURE__ */ new Set();
81948
+ state.constructorGroupDependencies.set(ownerGroup, dependencies);
81949
+ }
81950
+ dependencies.add(receiverGroup);
81650
81951
  }
81651
- dependencies.add(receiverGroup);
81652
81952
  }
81653
- }
81654
81953
  const markConstructorGroupEscaped = (rootId) => {
81655
81954
  if (escapedRoots.has(rootId)) return;
81656
81955
  escapedRoots.add(rootId);
@@ -81847,7 +82146,7 @@ function finalizeConstructorAllocations(ctx, returnValue, ownsInvocationState, r
81847
82146
  const survivingWrites = state.writes.filter((write) => {
81848
82147
  if (write.kind === "changeListener") {
81849
82148
  if (collectedIds.has(write.ownerId)) return false;
81850
- return write.listener.valueId === null || !collectedIds.has(write.listener.valueId);
82149
+ return write.operation === "clear" || write.listener.valueId === null || !collectedIds.has(write.listener.valueId);
81851
82150
  }
81852
82151
  const destination = write.resolvedDestination;
81853
82152
  const destinationId = destination !== void 0 && "containerValueId" in destination ? destination.containerValueId : void 0;
@@ -82308,6 +82607,7 @@ function validateTryInstruction(instruction) {
82308
82607
  function evalBody(body, scope, ctx, options, preparedFrame) {
82309
82608
  const state = ctx.__executionState;
82310
82609
  if (!state) return evalInstructions(body.instructions, scope, ctx, options);
82610
+ const hostBody = state.frames.length === 0 && ctx.__getterSubscriptions !== void 0;
82311
82611
  state.frames.push(
82312
82612
  preparedFrame ?? {
82313
82613
  name: body.source?.name ?? "<body>",
@@ -82316,7 +82616,14 @@ function evalBody(body, scope, ctx, options, preparedFrame) {
82316
82616
  }
82317
82617
  );
82318
82618
  try {
82319
- return evalInstructions(body.instructions, scope, ctx, options);
82619
+ if (!hostBody)
82620
+ return evalInstructions(body.instructions, scope, ctx, options);
82621
+ const result = evalInstructions(body.instructions, scope, ctx, {
82622
+ ...options,
82623
+ dispatchesGetterChanges: true
82624
+ });
82625
+ dispatchGetterSubscriptions(ctx);
82626
+ return result;
82320
82627
  } catch (error) {
82321
82628
  attachNeoScriptTrace(error, state);
82322
82629
  throw error;
@@ -82480,7 +82787,8 @@ function evalInstructions(instructions, scope, ctx, options) {
82480
82787
  break;
82481
82788
  }
82482
82789
  case "addChangeListener" /* addChangeListener */:
82483
- case "removeChangeListener" /* removeChangeListener */: {
82790
+ case "removeChangeListener" /* removeChangeListener */:
82791
+ case "clearChangeListeners" /* clearChangeListeners */: {
82484
82792
  evalChangeListenerInstruction(ins, scope, ctx, options);
82485
82793
  break;
82486
82794
  }
@@ -82695,6 +83003,8 @@ function evalInstructions(instructions, scope, ctx, options) {
82695
83003
  return { kind: "continue" };
82696
83004
  }
82697
83005
  void touchUnknown(ins);
83006
+ if (options.dispatchesGetterChanges === true)
83007
+ dispatchGetterSubscriptions(ctx);
82698
83008
  } catch (error) {
82699
83009
  positionNeoScriptFrame(ctx, ins.source);
82700
83010
  if (ctx.__executionState)
@@ -82778,6 +83088,30 @@ function resolveNeoScriptChangeListenerSlot(ownerId, memberId, ctx) {
82778
83088
  typeInfo: changeListenerObservedType(raw, member, env, ctx)
82779
83089
  };
82780
83090
  }
83091
+ function resolveNeoScriptGetterListenerSlot(ownerId, memberId, ctx) {
83092
+ const owner = evalValueById(
83093
+ ctx,
83094
+ ownerId,
83095
+ ctx.__runtimeSessionValues,
83096
+ ctx.__valueOverlay
83097
+ );
83098
+ if (owner === null || owner.value === null) return null;
83099
+ const entry = changeListenerMemberOnOwner(owner, memberId, ctx);
83100
+ if (entry === void 0) return null;
83101
+ const member = evalMemberById(ctx.vm, entry.memberId);
83102
+ if (member === null || !isMemberNSProperty(member)) return null;
83103
+ if (isStaticMember(member)) return null;
83104
+ const env = runtimeGenericEnvForValueRow(owner, ctx, /* @__PURE__ */ new Set());
83105
+ const typeInfo = substituteRuntimeTypeInfo(member.returnTypeInfo, env, ctx);
83106
+ if (typeInfo.type === NS_TYPE_VOID) return null;
83107
+ return {
83108
+ owner,
83109
+ member,
83110
+ memberId: evaluatorResolutionCache(ctx).canonicalMemberId(member.id),
83111
+ schemaKey: entry.schemaKey,
83112
+ typeInfo
83113
+ };
83114
+ }
82781
83115
  function changeListenerObservedType(raw, member, env, ctx) {
82782
83116
  const runtime = runtimeTypeInfoForMember(raw, env, ctx, /* @__PURE__ */ new Set());
82783
83117
  if (!isMemberLookup(member)) return runtime;
@@ -82800,6 +83134,18 @@ function changeListenerObservedType(raw, member, env, ctx) {
82800
83134
  required: isRequiredMember(member)
82801
83135
  };
82802
83136
  }
83137
+ function readNeoScriptChangeListenerValue(slot, ctx) {
83138
+ const result = dispatchSchemaMember(
83139
+ readRowValue(slot.owner, ctx),
83140
+ slot.schemaKey,
83141
+ ctx
83142
+ );
83143
+ if (result.kind !== "ok")
83144
+ throw new NSGetterRuntimeError(
83145
+ `OnChanged cannot read '${slot.owner.id}/${slot.memberId}'.`
83146
+ );
83147
+ return result.value;
83148
+ }
82803
83149
  function resolveNeoScriptChangeListenerHandler(slot, target, ctx) {
82804
83150
  const raw = evalMemberById(ctx.vm, target.memberId);
82805
83151
  if (raw === null) return null;
@@ -82861,15 +83207,29 @@ function evalChangeListenerInstruction(ins, scope, ctx, options) {
82861
83207
  throw new NSGetterRuntimeError(
82862
83208
  "OnChanged requires an existing logical owner."
82863
83209
  );
82864
- const slot = resolveNeoScriptChangeListenerSlot(
83210
+ const stored = resolveNeoScriptChangeListenerSlot(
82865
83211
  owner.id,
82866
83212
  ins.target.memberId,
82867
83213
  ctx
82868
83214
  );
83215
+ const getter = stored === null ? resolveNeoScriptGetterListenerSlot(owner.id, ins.target.memberId, ctx) : null;
83216
+ const slot = stored ?? getter;
82869
83217
  if (slot === null)
82870
83218
  throw new NSGetterRuntimeError(
82871
- `OnChanged member '${ins.target.memberId}' is not an observable stored member of owner '${owner.id}'.`
83219
+ `OnChanged member '${ins.target.memberId}' is not an observable member or getter of owner '${owner.id}'.`
82872
83220
  );
83221
+ if (ins.type === "clearChangeListeners" /* clearChangeListeners */) {
83222
+ if (getter !== null) clearGetterSubscription(getter, ctx);
83223
+ else
83224
+ options.writes.push({
83225
+ kind: "changeListener",
83226
+ target: ins.target,
83227
+ ownerId: owner.id,
83228
+ memberId: slot.memberId,
83229
+ operation: "clear"
83230
+ });
83231
+ return;
83232
+ }
82873
83233
  const listener = evalPointer(ins.listener, scope, ctx);
82874
83234
  if (!isMemberDelegateTarget(listener))
82875
83235
  throw new NSGetterRuntimeError(
@@ -82880,6 +83240,15 @@ function evalChangeListenerInstruction(ins, scope, ctx, options) {
82880
83240
  throw new NSGetterRuntimeError(
82881
83241
  `OnChanged handler '${listener.memberId}' cannot accept the value of '${slot.member.name}' on '${owner.id}'.`
82882
83242
  );
83243
+ if (getter !== null) {
83244
+ editGetterSubscription(
83245
+ getter,
83246
+ handler.target,
83247
+ ins.type === "addChangeListener" /* addChangeListener */,
83248
+ ctx
83249
+ );
83250
+ return;
83251
+ }
82883
83252
  options.writes.push({
82884
83253
  kind: "changeListener",
82885
83254
  target: ins.target,
@@ -82889,6 +83258,106 @@ function evalChangeListenerInstruction(ins, scope, ctx, options) {
82889
83258
  listener: handler.target
82890
83259
  });
82891
83260
  }
83261
+ function editGetterSubscription(slot, target, add, ctx) {
83262
+ if ((ctx.__executionState?.constructionStack.length ?? 0) > 0)
83263
+ throw new NSGetterRuntimeError(
83264
+ "A getter subscription lives only for the play session, so a constructor can't make one. Subscribe from a function."
83265
+ );
83266
+ const subscriptions = ctx.__getterSubscriptions;
83267
+ if (subscriptions === void 0) return;
83268
+ let subscription = subscriptions.get(slot.owner.id, slot.memberId);
83269
+ const found = subscription?.targets.findIndex(
83270
+ (existing) => existing.memberId === target.memberId && existing.valueId === target.valueId
83271
+ ) ?? -1;
83272
+ if (!add) {
83273
+ if (subscription === void 0 || found < 0) return;
83274
+ subscription.targets.splice(found, 1);
83275
+ if (subscription.targets.length === 0) subscriptions.drop(subscription);
83276
+ return;
83277
+ }
83278
+ if (found >= 0) return;
83279
+ if (subscription === void 0) {
83280
+ subscription = new NeoScriptGetterSubscription(
83281
+ slot.owner.id,
83282
+ slot.memberId,
83283
+ slot.owner.classId
83284
+ );
83285
+ subscriptions.add(subscription);
83286
+ }
83287
+ subscription.targets.push(target);
83288
+ }
83289
+ function clearGetterSubscription(slot, ctx) {
83290
+ if ((ctx.__executionState?.constructionStack.length ?? 0) > 0)
83291
+ throw new NSGetterRuntimeError(
83292
+ "A getter subscription lives only for the play session, so a constructor can't clear one. Clear from a function."
83293
+ );
83294
+ const subscriptions = ctx.__getterSubscriptions;
83295
+ const subscription = subscriptions?.get(slot.owner.id, slot.memberId);
83296
+ if (subscriptions === void 0 || subscription === void 0) return;
83297
+ subscription.targets.length = 0;
83298
+ subscriptions.drop(subscription);
83299
+ }
83300
+ function dispatchGetterSubscriptions(ctx) {
83301
+ const subscriptions = ctx.__getterSubscriptions;
83302
+ if (subscriptions === void 0) return;
83303
+ const draining = subscriptions.takePending();
83304
+ let next = 0;
83305
+ try {
83306
+ while (next < draining.length)
83307
+ dispatchGetterSubscription(draining[next++], subscriptions, ctx);
83308
+ } finally {
83309
+ subscriptions.requeue(draining, next);
83310
+ }
83311
+ }
83312
+ function dispatchGetterSubscription(subscription, subscriptions, ctx) {
83313
+ subscription.queued = false;
83314
+ const notify = !subscription.arming;
83315
+ subscription.arming = false;
83316
+ if (subscription.targets.length === 0) return;
83317
+ const slot = resolveNeoScriptGetterListenerSlot(
83318
+ subscription.rowId,
83319
+ subscription.getterId,
83320
+ ctx
83321
+ );
83322
+ if (slot === null || slot.owner.classId !== subscription.classId) {
83323
+ subscriptions.drop(subscription);
83324
+ return;
83325
+ }
83326
+ const reads = new InitializerReadSet();
83327
+ let value;
83328
+ try {
83329
+ value = readNeoScriptChangeListenerValue(slot, {
83330
+ ...ctx,
83331
+ __getterReads: reads.valueIds,
83332
+ containerDependencies: reads.containerIds,
83333
+ staticMemberDependencies: reads.staticMemberIds,
83334
+ worldQueryDependencies: reads.worldQueries,
83335
+ onUnattributableValueRead: reads.recordUnattributableValueRead
83336
+ });
83337
+ } finally {
83338
+ subscriptions.setReads(subscription, reads);
83339
+ }
83340
+ if (!notify) return;
83341
+ const owner = readRowValue(slot.owner, ctx);
83342
+ subscription.handling = true;
83343
+ try {
83344
+ for (const target of [...subscription.targets]) {
83345
+ const handler = resolveNeoScriptChangeListenerHandler(slot, target, ctx);
83346
+ if (handler === null) continue;
83347
+ invokeDelegateValue(
83348
+ handler.invocation,
83349
+ [value],
83350
+ ctx,
83351
+ void 0,
83352
+ owner,
83353
+ "discarded",
83354
+ () => dispatchGetterSubscriptions(ctx)
83355
+ );
83356
+ }
83357
+ } finally {
83358
+ subscription.handling = false;
83359
+ }
83360
+ }
82892
83361
  function evalActionListenerInstruction(ins, scope, ctx, options) {
82893
83362
  const operator = ins.type === "addActionListener" /* addActionListener */ ? "+=" : "-=";
82894
83363
  if (ins.target.pointer.type === "variable" /* variable */) {
@@ -83065,6 +83534,7 @@ function applyOverlayStaticAssignment(memberId, value, ctx) {
83065
83534
  );
83066
83535
  }
83067
83536
  const bindings = writableStaticBindingMap(member, ctx);
83537
+ ctx.__getterSubscriptions?.noteStaticBinding(member.id);
83068
83538
  if (value === null || value === void 0) {
83069
83539
  if (isRequiredMember(member)) {
83070
83540
  throw new NSGetterRuntimeError(
@@ -83103,7 +83573,7 @@ function applyOverlayStaticAssignment(memberId, value, ctx) {
83103
83573
  `Static member '${member.name}' is bound to missing value '${currentId}'.`
83104
83574
  );
83105
83575
  }
83106
- ctx.beforeValueWrite?.(row2);
83576
+ observeValueWrite(ctx, row2);
83107
83577
  row2.value = value;
83108
83578
  row2.updatedAt = Date.now();
83109
83579
  invalidateEvaluatorIndexes(ctx);
@@ -89904,14 +90374,14 @@ function evalVariantApply(info, scope, ctx) {
89904
90374
  });
89905
90375
  for (const valueId of resolution.clearedValueIds) {
89906
90376
  const previous = readNeoScriptRuntimeRow(ctx, valueId);
89907
- if (previous) ctx.beforeValueWrite?.(previous);
90377
+ if (previous) observeValueWrite(ctx, previous);
89908
90378
  deleteRuntimeSessionValue(ctx, valueId);
89909
90379
  ctx.__valueOverlay?.delete(valueId);
89910
90380
  }
89911
90381
  for (const row of resolution.rows) {
89912
90382
  if (row.id === rootRow.id) continue;
89913
90383
  const previous = readNeoScriptRuntimeRow(ctx, row.id);
89914
- if (previous) ctx.beforeValueWrite?.(previous);
90384
+ if (previous) observeValueWrite(ctx, previous);
89915
90385
  ctx.__valueOverlay?.set(row.id, deepClonePlainData(row));
89916
90386
  }
89917
90387
  if (typeof receiver !== "object" || receiver === null || Array.isArray(receiver) || typeof resolution.root.value !== "object" || resolution.root.value === null || Array.isArray(resolution.root.value)) {
@@ -91129,7 +91599,7 @@ function cloneClassValueGraph(receiver, expectedClassId, ctx) {
91129
91599
  const edit = editChangeListenerSet(
91130
91600
  entry?.[write.memberId],
91131
91601
  [],
91132
- { kind: write.operation, target: write.listener },
91602
+ changeListenerWriteEdit(write),
91133
91603
  { inheritedComplete: false }
91134
91604
  );
91135
91605
  if (!edit.changed) continue;
@@ -91373,6 +91843,8 @@ var init_evaluateNSGetter = __esm({
91373
91843
  init_cell_pattern();
91374
91844
  init_cell_pattern2();
91375
91845
  init_world_grid_query();
91846
+ init_initializer_context();
91847
+ init_getter_subscriptions();
91376
91848
  init_classes();
91377
91849
  init_core();
91378
91850
  init_construction_requirements();
@@ -107269,6 +107741,7 @@ var init_neoscript2 = __esm({
107269
107741
  init_evaluateNSGetter();
107270
107742
  init_evaluateInitializer();
107271
107743
  init_initializer_context();
107744
+ init_getter_subscriptions();
107272
107745
  }
107273
107746
  });
107274
107747
 
@@ -108599,10 +109072,11 @@ function migrationMemberListenerRows(document, writes, copied) {
108599
109072
  const map = root.changeListeners;
108600
109073
  const entry = map[write.ownerId] ?? {};
108601
109074
  const inherited = reader.listenerDefaultsForOwner(write.ownerId)?.[write.memberId] ?? [];
108602
- const edit = editChangeListenerSet(entry[write.memberId], inherited, {
108603
- kind: write.operation,
108604
- target: write.listener
108605
- });
109075
+ const edit = editChangeListenerSet(
109076
+ entry[write.memberId],
109077
+ inherited,
109078
+ changeListenerWriteEdit(write)
109079
+ );
108606
109080
  if (!edit.changed) continue;
108607
109081
  if (edit.targets === void 0) delete entry[write.memberId];
108608
109082
  else entry[write.memberId] = edit.targets;
@@ -108635,6 +109109,7 @@ var init_project_migration_member_listeners = __esm({
108635
109109
  init_members();
108636
109110
  init_project_root_members();
108637
109111
  init_member_change_listeners();
109112
+ init_listener_write_index();
108638
109113
  init_authored_listener_endpoints();
108639
109114
  }
108640
109115
  });
@@ -154561,7 +155036,7 @@ var init_project_transaction = __esm({
154561
155036
  }
154562
155037
  });
154563
155038
 
154564
- // node_modules/fflate/esm/index.mjs
155039
+ // ../node_modules/fflate/esm/index.mjs
154565
155040
  import { createRequire } from "module";
154566
155041
  function gzipSync(data, opts) {
154567
155042
  if (!opts)
@@ -154573,7 +155048,7 @@ function gzipSync(data, opts) {
154573
155048
  }
154574
155049
  var require2, _a, Worker4, isMarkedAsUntransferable, u8, u16, i32, fleb, fdeb, clim, freb, _a, fl, revfl, _b, fd, revfd, rev, x, i, hMap, flt, i, i, i, i, fdt, i, flm, fdm, shft, slc, wbits, wbits16, hTree, ln, lc, clen, wfblk, wblk, deo, et, dflt, crct, crc, dopt, wbytes, gzh, gzhl, td, tds;
154575
155050
  var init_esm = __esm({
154576
- "node_modules/fflate/esm/index.mjs"() {
155051
+ "../node_modules/fflate/esm/index.mjs"() {
154577
155052
  require2 = createRequire("/");
154578
155053
  try {
154579
155054
  _a = require2("worker_threads"), Worker4 = _a.Worker, isMarkedAsUntransferable = _a.isMarkedAsUntransferable;
@@ -156718,7 +157193,7 @@ var init_neoscript_file_references = __esm({
156718
157193
  }
156719
157194
  });
156720
157195
 
156721
- // node_modules/@formatjs/icu-messageformat-parser/node_modules/@formatjs/icu-skeleton-parser/index.js
157196
+ // ../node_modules/@formatjs/icu-skeleton-parser/index.js
156722
157197
  function parseDateTimeSkeleton(skeleton) {
156723
157198
  const result = {};
156724
157199
  skeleton.replace(DATE_TIME_REGEX, (match) => {
@@ -157087,7 +157562,7 @@ function parseNumberSkeleton(tokens) {
157087
157562
  }
157088
157563
  var DATE_TIME_REGEX, WHITE_SPACE_REGEX, FRACTION_PRECISION_REGEX, SIGNIFICANT_PRECISION_REGEX, INTEGER_WIDTH_REGEX, CONCISE_INTEGER_WIDTH_REGEX;
157089
157564
  var init_icu_skeleton_parser = __esm({
157090
- "node_modules/@formatjs/icu-messageformat-parser/node_modules/@formatjs/icu-skeleton-parser/index.js"() {
157565
+ "../node_modules/@formatjs/icu-skeleton-parser/index.js"() {
157091
157566
  DATE_TIME_REGEX = /(?:[Eec]{1,6}|G{1,5}|[Qq]{1,5}|(?:[yYur]+|U{1,5})|[ML]{1,5}|d{1,2}|D{1,3}|F{1}|[abB]{1,5}|[hkHK]{1,2}|w{1,2}|W{1}|m{1,2}|s{1,2}|[zZOvVxX]{1,4})(?=([^']*'[^']*')*[^']*$)/g;
157092
157567
  WHITE_SPACE_REGEX = /[\t-\r \x85\u200E\u200F\u2028\u2029]/i;
157093
157568
  FRACTION_PRECISION_REGEX = /^\.(?:(0+)(\*)?|(#+)|(0+)(#+))$/g;
@@ -157097,7 +157572,7 @@ var init_icu_skeleton_parser = __esm({
157097
157572
  }
157098
157573
  });
157099
157574
 
157100
- // node_modules/@formatjs/icu-messageformat-parser/index.js
157575
+ // ../node_modules/@formatjs/icu-messageformat-parser/index.js
157101
157576
  function isArgumentElement(el) {
157102
157577
  return el.type === 1;
157103
157578
  }
@@ -157265,7 +157740,7 @@ function parse3(message, opts = {}) {
157265
157740
  }
157266
157741
  var ErrorKind, SPACE_SEPARATOR_REGEX, IDENTIFIER_PREFIX_REGEX, timeData, SPACE_SEPARATOR_START_REGEX, SPACE_SEPARATOR_END_REGEX, hasNativeFromEntries, hasTrimStart, hasTrimEnd, fromEntries, trimStart, trimEnd, IDENTIFIER_PREFIX_RE, Parser2;
157267
157742
  var init_icu_messageformat_parser = __esm({
157268
- "node_modules/@formatjs/icu-messageformat-parser/index.js"() {
157743
+ "../node_modules/@formatjs/icu-messageformat-parser/index.js"() {
157269
157744
  init_icu_skeleton_parser();
157270
157745
  ErrorKind = /* @__PURE__ */ (function(ErrorKind2) {
157271
157746
  ErrorKind2[ErrorKind2["EXPECT_ARGUMENT_CLOSING_BRACE"] = 1] = "EXPECT_ARGUMENT_CLOSING_BRACE";
@@ -171758,7 +172233,7 @@ var init_project_unity_export_types = __esm({
171758
172233
  "../src/models/exports/project-unity-export-types.ts"() {
171759
172234
  "use strict";
171760
172235
  init_unity_codegen_revision();
171761
- PROJECT_UNITY_EXPORT_SCHEMA_VERSION = 39;
172236
+ PROJECT_UNITY_EXPORT_SCHEMA_VERSION = 41;
171762
172237
  }
171763
172238
  });
171764
172239
 
@@ -182955,7 +183430,8 @@ function makeEvaluatorContext(rawDocument, interceptor) {
182955
183430
  rootValue: base.rootValue,
182956
183431
  dialogueContext: null,
182957
183432
  callInterceptor: interceptor,
182958
- nativeFunctionErrorCheckBehavior: "strict"
183433
+ nativeFunctionErrorCheckBehavior: "strict",
183434
+ getterDispatch: "afterEntryStatement" /* AfterEntryStatement */
182959
183435
  });
182960
183436
  }
182961
183437
  function documentRecord(document) {
@@ -183829,6 +184305,7 @@ function copyMocks(source, target) {
183829
184305
  function cloneTestEnvironment(source) {
183830
184306
  const cloned = createTestEnvironment(snapshotEnvironmentDocument(source));
183831
184307
  copyMocks(source, cloned);
184308
+ cloned.context.__getterSubscriptions = source.context.__getterSubscriptions?.clone();
183832
184309
  return cloned;
183833
184310
  }
183834
184311
  function runCallback(environment, delegate, deadlineMs) {
@@ -188743,7 +189220,7 @@ var init_member_change_listener_runtime = __esm({
188743
189220
  for (const ownerId of owners)
188744
189221
  for (const write of writes.get(ownerId) ?? []) {
188745
189222
  const slot = this.slot(write.ownerId, write.memberId);
188746
- if (slot === null || this.targetTier(slot, write.listener)?.tier !== "save")
189223
+ if (slot === null || write.operation !== "clear" && this.targetTier(slot, write.listener)?.tier !== "save")
188747
189224
  continue;
188748
189225
  const entry = map[write.ownerId] ?? {};
188749
189226
  const inherited = this.baseline(slot, "save");
@@ -188757,7 +189234,7 @@ var init_member_change_listener_runtime = __esm({
188757
189234
  const edited = editChangeListenerSet(
188758
189235
  durable,
188759
189236
  inherited.targets,
188760
- { kind: write.operation, target: write.listener },
189237
+ changeListenerWriteEdit(write),
188761
189238
  { inheritedComplete: inherited.complete }
188762
189239
  );
188763
189240
  if (!edited.changed) continue;
@@ -188849,41 +189326,48 @@ var init_member_change_listener_runtime = __esm({
188849
189326
  throw new Error(
188850
189327
  `OnChanged slot '${write.ownerId}/${write.memberId}' is not writable in this runtime.`
188851
189328
  );
188852
- let target;
189329
+ const targets = [];
188853
189330
  if (write.operation === "reset")
188854
- target = { tier: write.tier, edit: { kind: "reset" } };
188855
- else {
189331
+ targets.push({ tier: write.tier, edit: { kind: "reset" } });
189332
+ else if (write.operation === "clear") {
189333
+ const edit = changeListenerWriteEdit(write);
189334
+ if (slot.storage !== 3 /* Session */)
189335
+ targets.push({ tier: "save", edit });
189336
+ targets.push({ tier: "session", edit });
189337
+ } else {
188856
189338
  const resolved = this.targetTier(slot, write.listener);
188857
189339
  if (resolved === null)
188858
189340
  throw new Error(
188859
189341
  `OnChanged handler '${write.listener.memberId}' is incompatible with '${write.ownerId}/${write.memberId}'.`
188860
189342
  );
188861
189343
  const inSession = current("session", slot.rootId, slot.owner.id)?.[slot.memberId]?.some(
188862
- (existing2) => nsActionListenerIdentity(existing2) === nsActionListenerIdentity(resolved.target)
189344
+ (existing) => nsActionListenerIdentity(existing) === nsActionListenerIdentity(resolved.target)
188863
189345
  );
188864
- target = {
189346
+ targets.push({
188865
189347
  tier: inSession ? "session" : resolved.tier,
188866
189348
  edit: { kind: write.operation, target: resolved.target }
188867
- };
189349
+ });
189350
+ }
189351
+ for (const target of targets) {
189352
+ this.host.assertWritable(slot, target.tier);
189353
+ const existing = current(target.tier, slot.rootId, slot.owner.id);
189354
+ const explicit = existing !== void 0 && Object.hasOwn(existing, slot.memberId) ? existing[slot.memberId] : void 0;
189355
+ const baseline = this.baseline(
189356
+ slot,
189357
+ target.tier,
189358
+ target.tier !== "save" || !adopting.has(slot.owner.id)
189359
+ );
189360
+ const result = editChangeListenerSet(
189361
+ explicit,
189362
+ baseline.targets,
189363
+ target.edit,
189364
+ { inheritedComplete: baseline.complete }
189365
+ );
189366
+ if (!result.changed) continue;
189367
+ const entry = editOwner(target.tier, slot.rootId, slot.owner.id);
189368
+ if (result.targets === void 0) delete entry[slot.memberId];
189369
+ else setOwnField(entry, slot.memberId, result.targets);
188868
189370
  }
188869
- this.host.assertWritable(slot, target.tier);
188870
- const existing = current(target.tier, slot.rootId, slot.owner.id);
188871
- const explicit = existing !== void 0 && Object.hasOwn(existing, slot.memberId) ? existing[slot.memberId] : void 0;
188872
- const baseline = this.baseline(
188873
- slot,
188874
- target.tier,
188875
- target.tier !== "save" || !adopting.has(slot.owner.id)
188876
- );
188877
- const result = editChangeListenerSet(
188878
- explicit,
188879
- baseline.targets,
188880
- target.edit,
188881
- { inheritedComplete: baseline.complete }
188882
- );
188883
- if (!result.changed) continue;
188884
- const entry = editOwner(target.tier, slot.rootId, slot.owner.id);
188885
- if (result.targets === void 0) delete entry[slot.memberId];
188886
- else setOwnField(entry, slot.memberId, result.targets);
188887
189371
  }
188888
189372
  for (const tier of ["save", "session"]) {
188889
189373
  for (const [rootId, changed] of pending[tier]) {
@@ -189757,7 +190241,7 @@ var init_dialogue_dryrun_listeners = __esm({
189757
190241
  this.copied = context.__copiedChangeListeners ?? this.copied;
189758
190242
  this.runtime.retireOwners(context.__removedValueIds ?? []);
189759
190243
  const listenerWrites = evaluatedWrites(result).filter(
189760
- (write) => write.kind === "changeListener" && !context.__removedValueIds?.has(write.ownerId) && (write.listener.valueId === null || !context.__removedValueIds?.has(write.listener.valueId))
190244
+ (write) => write.kind === "changeListener" && !context.__removedValueIds?.has(write.ownerId) && (write.operation === "clear" || write.listener.valueId === null || !context.__removedValueIds?.has(write.listener.valueId))
189761
190245
  );
189762
190246
  const adopted = new Set(
189763
190247
  (result.createdSessionValues ?? []).map((row) => row.id)
@@ -191491,7 +191975,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
191491
191975
  async function main() {
191492
191976
  const args = parseArgs(process.argv.slice(2));
191493
191977
  if (args.command === "--version") {
191494
- console.log("0.73.2");
191978
+ console.log("0.75.0");
191495
191979
  return;
191496
191980
  }
191497
191981
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {