@neocompose/cli 0.73.1 → 0.74.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();
@@ -20692,6 +20692,13 @@ function span(identity2) {
20692
20692
  function materializedId(identity2, kind, name) {
20693
20693
  return materializedIdentityId(identity2, kind, name);
20694
20694
  }
20695
+ function materializedMemberId(owner, member) {
20696
+ return materializedId(
20697
+ member,
20698
+ owner.kind === "class" ? "member" : "interface-member",
20699
+ `${owner.name}.${member.name}`
20700
+ );
20701
+ }
20695
20702
  function materializedIdentityId(identity2, kind, name) {
20696
20703
  return identity2.id ?? `__pending__:${kind}:${encodeURIComponent(identity2.source.uri)}:${identity2.source.range.start.line}:${identity2.source.range.start.character}:${encodeURIComponent(name)}`;
20697
20704
  }
@@ -28484,6 +28491,15 @@ function inspectListenerAnnotation(annotation2, parameters) {
28484
28491
  return;
28485
28492
  }
28486
28493
  const parameter4 = parameters.byName.get(name);
28494
+ if (!parameter4 && parameters.getters.has(name)) {
28495
+ errors.push(
28496
+ new CompileError(
28497
+ `'${name}' is a getter. Getter listeners can't be authored. Subscribe with '+=' from a function.`,
28498
+ expression.pos
28499
+ )
28500
+ );
28501
+ return;
28502
+ }
28487
28503
  if (!parameter4) {
28488
28504
  errors.push(
28489
28505
  new CompileError(
@@ -28527,7 +28543,10 @@ function createListenerAnnotationProvider(project, context = {}) {
28527
28543
  if (cached) return cached;
28528
28544
  const parameters = [];
28529
28545
  const byName = /* @__PURE__ */ new Map();
28546
+ const getters = /* @__PURE__ */ new Set();
28530
28547
  for (const symbol of declaration.members) {
28548
+ if (symbol.kind === "property" && symbol.computed === true && symbol.static !== true)
28549
+ getters.add(symbol.name);
28531
28550
  if (!isChangeListenerSymbol(symbol)) continue;
28532
28551
  if (memberAccessFailure({ project, context, owner: declaration, symbol }))
28533
28552
  continue;
@@ -28551,7 +28570,8 @@ function createListenerAnnotationProvider(project, context = {}) {
28551
28570
  const result = {
28552
28571
  namedOnly: true,
28553
28572
  parameters,
28554
- byName
28573
+ byName,
28574
+ getters
28555
28575
  };
28556
28576
  cache.set(key, result);
28557
28577
  return result;
@@ -32771,9 +32791,26 @@ var init_strict_resolver = __esm({
32771
32791
  "OnChanged requires a declared member, not a local variable.",
32772
32792
  statement.pos
32773
32793
  );
32794
+ if (observed.pointer.type === "staticMember" /* StaticMember */ || observed.pointer.type === "callGetter" /* CallGetter */ && observed.pointer.receiver.kind === "static") {
32795
+ const access = statement.target.receiver;
32796
+ const name = access.kind === "member" && access.receiver.kind === "ident" ? `${access.receiver.name}.${member.name}` : member.name;
32797
+ throw new CompileError(
32798
+ `OnChanged requires an instance member. '${name}' is static.`,
32799
+ statement.pos
32800
+ );
32801
+ }
32802
+ if (observed.pointer.type === "callGetter" /* CallGetter */)
32803
+ return this.resolveGetterSubscription(
32804
+ statement,
32805
+ statement.target.receiver,
32806
+ observed,
32807
+ observed.pointer,
32808
+ member,
32809
+ scope
32810
+ );
32774
32811
  if (observed.pointer.type !== "keyOf" /* KeyOf */)
32775
32812
  throw new CompileError(
32776
- "OnChanged requires a stored instance member, not a static member.",
32813
+ "OnChanged requires a declared member slot.",
32777
32814
  statement.pos
32778
32815
  );
32779
32816
  if (!observed.pointer.memberId)
@@ -32847,6 +32884,60 @@ var init_strict_resolver = __esm({
32847
32884
  listener
32848
32885
  };
32849
32886
  }
32887
+ /**
32888
+ * P106. A getter subscription lives only in the running client's memory:
32889
+ * nothing persists it, so construction replay would silently drop one.
32890
+ */
32891
+ resolveGetterSubscription(statement, getterRead, observed, pointer, member, scope) {
32892
+ if (pointer.dispatch === "base") {
32893
+ throw new CompileError(
32894
+ `OnChanged can't observe a base getter. Subscribe to '${member.name}' on the instance.`,
32895
+ statement.pos
32896
+ );
32897
+ }
32898
+ if (pointer.receiver.kind !== "instance") {
32899
+ throw new CompileError(
32900
+ `OnChanged requires an instance receiver for getter '${member.name}'.`,
32901
+ statement.pos
32902
+ );
32903
+ }
32904
+ const kind = this.context.kind;
32905
+ if (isNeoScriptConstructionDocumentKind(kind) || kind === "migration") {
32906
+ throw new CompileError(
32907
+ `A getter subscription lives only for the play session, so ${dialogueBodyKindLabel(kind)} can't make one. Subscribe from a function.`,
32908
+ statement.pos
32909
+ );
32910
+ }
32911
+ if (!statement.value) {
32912
+ throw new CompileError(
32913
+ "OnChanged subscription requires a handler member.",
32914
+ statement.pos
32915
+ );
32916
+ }
32917
+ const getterType = this.assignmentStorageType(
32918
+ getterRead,
32919
+ observed,
32920
+ null,
32921
+ scope
32922
+ );
32923
+ const listener = this.resolveActionListener(
32924
+ statement.value,
32925
+ { kind: "action", parameterTypes: [getterType] },
32926
+ scope,
32927
+ true,
32928
+ true
32929
+ );
32930
+ return {
32931
+ type: statement.op === "+=" ? "addChangeListener" /* AddChangeListener */ : "removeChangeListener" /* RemoveChangeListener */,
32932
+ target: {
32933
+ owner: pointer.receiver.pointer,
32934
+ memberId: member.canonicalMemberId ?? pointer.memberId,
32935
+ typeInfo: toWireType(getterType, this.project),
32936
+ writability: "session" /* Session */
32937
+ },
32938
+ listener
32939
+ };
32940
+ }
32850
32941
  /**
32851
32942
  * P62 §3.2. `+=` / `-=` on an NSAction member are subscription operators,
32852
32943
  * not arithmetic: the listener set *is* the member's value, so each one
@@ -43884,6 +43975,16 @@ function validateAnnotations(uri, annotations, context, environment, diagnostics
43884
43975
  continue;
43885
43976
  }
43886
43977
  }
43978
+ if (annotation2.name === "settings" && field.name === "collection" && "type" in context) {
43979
+ validateLookupCollectionType(
43980
+ uri,
43981
+ argument2.text,
43982
+ argument2.range,
43983
+ context.type,
43984
+ environment,
43985
+ diagnostics
43986
+ );
43987
+ }
43887
43988
  validateExpressionText(
43888
43989
  uri,
43889
43990
  argument2.text,
@@ -43897,6 +43998,37 @@ function validateAnnotations(uri, annotations, context, environment, diagnostics
43897
43998
  }
43898
43999
  }
43899
44000
  }
44001
+ function validateLookupCollectionType(uri, collection, range2, declared, environment, diagnostics) {
44002
+ const path = collection.trim().split(".");
44003
+ const [owner, name] = path;
44004
+ if (path.length !== 2 || owner === void 0 || name === void 0) return;
44005
+ const field = sourceClassMemberForType({ name: owner }, name, environment);
44006
+ if (!field) return;
44007
+ const collectionType = substituteSemanticType(
44008
+ semanticType(field.member.type),
44009
+ field.bindings
44010
+ );
44011
+ const entryIndex = collectionElementIndex(collectionType.name);
44012
+ const entry = entryIndex === null ? void 0 : collectionType.arguments?.[entryIndex];
44013
+ const target = declared.name === "List" || declared.name === "Set" ? declared.arguments?.[0] : declared;
44014
+ if (!entry || !target) return;
44015
+ if (!environment.types.has(target.name) || !environment.types.has(entry.name))
44016
+ return;
44017
+ if (constructionTypesAssignable(
44018
+ { ...target, nullable: false },
44019
+ { ...entry, nullable: false },
44020
+ environment.globals,
44021
+ environment
44022
+ ))
44023
+ return;
44024
+ pushDiagnostic(
44025
+ diagnostics,
44026
+ uri,
44027
+ range2,
44028
+ "lookup-collection-type-mismatch",
44029
+ `Lookup declares ${describeType(target)}, but collection ${collection.trim()} holds ${describeType(entry)}.`
44030
+ );
44031
+ }
43900
44032
  function fieldsForAnnotation(name, context, environment) {
43901
44033
  if (name === "id") {
43902
44034
  return context.kind === "class" || context.kind === "schema" || context.kind === "member" || context.kind === "variantGlobal" || context.kind === "projectFile" || context.kind === "flowMember" || context.kind === "flowBinding" || context.kind === "flowGraph" ? [{ name: "id", type: "string" }] : null;
@@ -51641,7 +51773,7 @@ var init_registry = __esm({
51641
51773
  "../packages/manifest/schema-contract/registry.mjs"() {
51642
51774
  "use strict";
51643
51775
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
51644
- cliVersion: "0.73.1",
51776
+ cliVersion: "0.74.0",
51645
51777
  projectFileUploadBatchSize: 32,
51646
51778
  documentRecords: {
51647
51779
  member: {
@@ -54244,7 +54376,7 @@ var init_document_contracts2 = __esm({
54244
54376
  }
54245
54377
  });
54246
54378
 
54247
- // node_modules/valibot/dist/index.mjs
54379
+ // ../node_modules/valibot/dist/index.mjs
54248
54380
  // @__NO_SIDE_EFFECTS__
54249
54381
  function getGlobalConfig(config$1) {
54250
54382
  if (!config$1 && !store$4) return DEFAULT_CONFIG;
@@ -54990,11 +55122,6 @@ function variant(key, options, message$1) {
54990
55122
  }
54991
55123
  });
54992
55124
  }
54993
- function parse2(schema, input, config$1) {
54994
- const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig(config$1));
54995
- if (dataset.issues) throw new ValiError(dataset.issues);
54996
- return dataset.value;
54997
- }
54998
55125
  // @__NO_SIDE_EFFECTS__
54999
55126
  function pipe(...pipe$1) {
55000
55127
  return _standardSchema({
@@ -55022,27 +55149,15 @@ function safeParse(schema, input, config$1) {
55022
55149
  issues: dataset.issues
55023
55150
  };
55024
55151
  }
55025
- var store$4, DEFAULT_CONFIG, store$3, store$2, store$1, ValiError, EMOJI_REGEX, ABORT_EARLY_CONFIG;
55152
+ var store$4, DEFAULT_CONFIG, store$3, store$2, store$1, EMOJI_REGEX, ABORT_EARLY_CONFIG;
55026
55153
  var init_dist = __esm({
55027
- "node_modules/valibot/dist/index.mjs"() {
55154
+ "../node_modules/valibot/dist/index.mjs"() {
55028
55155
  DEFAULT_CONFIG = {
55029
55156
  lang: void 0,
55030
55157
  message: void 0,
55031
55158
  abortEarly: void 0,
55032
55159
  abortPipeEarly: void 0
55033
55160
  };
55034
- ValiError = class extends Error {
55035
- /**
55036
- * Creates a Valibot error with useful information.
55037
- *
55038
- * @param issues The error issues.
55039
- */
55040
- constructor(issues) {
55041
- super(issues[0].message);
55042
- this.name = "ValiError";
55043
- this.issues = issues;
55044
- }
55045
- };
55046
55161
  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");
55047
55162
  ABORT_EARLY_CONFIG = { abortEarly: true };
55048
55163
  }
@@ -55842,6 +55957,47 @@ var init_schema_member = __esm({
55842
55957
  }
55843
55958
  });
55844
55959
 
55960
+ // node_modules/valibot/dist/index.mjs
55961
+ // @__NO_SIDE_EFFECTS__
55962
+ function getGlobalConfig2(config$1) {
55963
+ if (!config$1 && !store$42) return DEFAULT_CONFIG2;
55964
+ return {
55965
+ lang: config$1?.lang ?? store$42?.lang,
55966
+ message: config$1?.message,
55967
+ abortEarly: config$1?.abortEarly ?? store$42?.abortEarly,
55968
+ abortPipeEarly: config$1?.abortPipeEarly ?? store$42?.abortPipeEarly
55969
+ };
55970
+ }
55971
+ function parse2(schema, input, config$1) {
55972
+ const dataset = schema["~run"]({ value: input }, /* @__PURE__ */ getGlobalConfig2(config$1));
55973
+ if (dataset.issues) throw new ValiError(dataset.issues);
55974
+ return dataset.value;
55975
+ }
55976
+ var store$42, DEFAULT_CONFIG2, ValiError, EMOJI_REGEX2;
55977
+ var init_dist2 = __esm({
55978
+ "node_modules/valibot/dist/index.mjs"() {
55979
+ DEFAULT_CONFIG2 = {
55980
+ lang: void 0,
55981
+ message: void 0,
55982
+ abortEarly: void 0,
55983
+ abortPipeEarly: void 0
55984
+ };
55985
+ ValiError = class extends Error {
55986
+ /**
55987
+ * Creates a Valibot error with useful information.
55988
+ *
55989
+ * @param issues The error issues.
55990
+ */
55991
+ constructor(issues) {
55992
+ super(issues[0].message);
55993
+ this.name = "ValiError";
55994
+ this.issues = issues;
55995
+ }
55996
+ };
55997
+ 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");
55998
+ }
55999
+ });
56000
+
55845
56001
  // src/project-manifest/record-adapters/codecs.ts
55846
56002
  function inferMemberOwners(classes, members) {
55847
56003
  const owners = /* @__PURE__ */ new Map();
@@ -57337,7 +57493,7 @@ var init_codecs = __esm({
57337
57493
  "use strict";
57338
57494
  init_document_contracts2();
57339
57495
  init_schema_member();
57340
- init_dist();
57496
+ init_dist2();
57341
57497
  init_docs_text2();
57342
57498
  MEMBER_KIND = {
57343
57499
  Null: 0,
@@ -66497,11 +66653,7 @@ function lowerClass(context, declaration) {
66497
66653
  const schema = {};
66498
66654
  const schemaKeyOrder = [];
66499
66655
  for (const memberDeclaration of declaration.members) {
66500
- const memberId = materializedId(
66501
- memberDeclaration,
66502
- "member",
66503
- `${declaration.name}.${memberDeclaration.name}`
66504
- );
66656
+ const memberId = materializedMemberId(declaration, memberDeclaration);
66505
66657
  schema[memberDeclaration.name] = memberId;
66506
66658
  schemaKeyOrder.push(memberDeclaration.name);
66507
66659
  const placement = { classId: id2, schemaKey: memberDeclaration.name };
@@ -67556,11 +67708,7 @@ function lowerInterface(context, declaration) {
67556
67708
  const id2 = materializedId(declaration, "interface", declaration.name);
67557
67709
  const base = context.baseInterfaces.get(id2);
67558
67710
  const members = declaration.members.map((member) => {
67559
- const memberId = materializedId(
67560
- member,
67561
- "interface-member",
67562
- `${declaration.name}.${member.name}`
67563
- );
67711
+ const memberId = materializedMemberId(declaration, member);
67564
67712
  const source = sourceIdentity(member, "interfaceMember", member.name);
67565
67713
  const accessModifier = effectiveSourceAccessModifier(
67566
67714
  member.modifiers,
@@ -68829,11 +68977,7 @@ function effectiveMemberIdByName(context, startClassId, name) {
68829
68977
  (member) => member.name === name
68830
68978
  );
68831
68979
  if (sourceMember !== void 0) {
68832
- return materializedId(
68833
- sourceMember,
68834
- "member",
68835
- `${sourceClass.name}.${sourceMember.name}`
68836
- );
68980
+ return materializedMemberId(sourceClass, sourceMember);
68837
68981
  }
68838
68982
  const sourceBase = sourceClass.baseTypes.find(
68839
68983
  (entry) => context.classIdsByName.has(entry.name)
@@ -73049,7 +73193,7 @@ function globalIds2(globals, kind) {
73049
73193
  return new Map(
73050
73194
  globals.map((global) => [
73051
73195
  global.name,
73052
- schemaIdentityId(global, kind, global.name)
73196
+ materializedId(global, kind, global.name)
73053
73197
  ])
73054
73198
  );
73055
73199
  }
@@ -73062,9 +73206,6 @@ function sourceId2(source, kind, name) {
73062
73206
  name
73063
73207
  );
73064
73208
  }
73065
- function schemaIdentityId(source, kind, name) {
73066
- return source.id ?? `__pending__:${kind}:${encodeURIComponent(source.source.uri)}:${source.source.range.start.line}:${source.source.range.start.character}:${encodeURIComponent(name)}`;
73067
- }
73068
73209
  function pendingId(kind, uri, owner, ordinal, name) {
73069
73210
  return `__pending__:${kind}:${encodeURIComponent(uri)}:${encodeURIComponent(owner)}:${ordinal}:${encodeURIComponent(name)}`;
73070
73211
  }
@@ -73107,6 +73248,7 @@ var SourceSchemaIndex;
73107
73248
  var init_dialogue_lower = __esm({
73108
73249
  "src/project-source/dialogue-lower.ts"() {
73109
73250
  "use strict";
73251
+ init_lower_support();
73110
73252
  init_dialogue_source_names();
73111
73253
  init_dialogue_group_paths();
73112
73254
  init_local_values();
@@ -73140,7 +73282,7 @@ var init_dialogue_lower = __esm({
73140
73282
  );
73141
73283
  this.classesById = new Map(
73142
73284
  analysis.schema.classes.map((entry) => [
73143
- schemaIdentityId(entry, "class", entry.name),
73285
+ materializedId(entry, "class", entry.name),
73144
73286
  entry
73145
73287
  ])
73146
73288
  );
@@ -73149,7 +73291,7 @@ var init_dialogue_lower = __esm({
73149
73291
  );
73150
73292
  this.interfacesById = new Map(
73151
73293
  analysis.schema.interfaces.map((entry) => [
73152
- schemaIdentityId(entry, "interface", entry.name),
73294
+ materializedId(entry, "interface", entry.name),
73153
73295
  entry
73154
73296
  ])
73155
73297
  );
@@ -73157,20 +73299,18 @@ var init_dialogue_lower = __esm({
73157
73299
  analysis.schema.enums.map((entry) => [entry.name, entry])
73158
73300
  );
73159
73301
  this.memberNamesById = new Map(
73160
- [
73161
- ...analysis.schema.classes.flatMap((entry) => entry.members),
73162
- ...analysis.schema.interfaces.flatMap((entry) => entry.members)
73163
- ].map((member) => [
73164
- schemaIdentityId(member, "member", member.name),
73165
- member.name
73166
- ])
73302
+ [...analysis.schema.classes, ...analysis.schema.interfaces].flatMap(
73303
+ (owner) => owner.members.map(
73304
+ (member) => [materializedMemberId(owner, member), member.name]
73305
+ )
73306
+ )
73167
73307
  );
73168
73308
  this.staticMemberExpressionsById = new Map(
73169
73309
  analysis.schema.classes.flatMap(
73170
73310
  (schemaClass2) => schemaClass2.members.flatMap(
73171
73311
  (member) => member.modifiers.includes("static") ? [
73172
73312
  [
73173
- schemaIdentityId(member, "member", member.name),
73313
+ materializedMemberId(schemaClass2, member),
73174
73314
  `${schemaClass2.name}.${member.name}`
73175
73315
  ]
73176
73316
  ] : []
@@ -73181,7 +73321,7 @@ var init_dialogue_lower = __esm({
73181
73321
  analysis.schema.enums.flatMap(
73182
73322
  (schemaEnum) => schemaEnum.options.map(
73183
73323
  (option) => [
73184
- schemaIdentityId(option, "enum-option", option.name),
73324
+ materializedId(option, "enum-option", option.name),
73185
73325
  option.name
73186
73326
  ]
73187
73327
  )
@@ -73245,13 +73385,13 @@ var init_dialogue_lower = __esm({
73245
73385
  };
73246
73386
  }
73247
73387
  classMember(schemaClass2, receiver, name, visited) {
73248
- const classId = schemaIdentityId(schemaClass2, "class", schemaClass2.name);
73388
+ const classId = materializedId(schemaClass2, "class", schemaClass2.name);
73249
73389
  if (visited.has(classId)) return null;
73250
73390
  visited.add(classId);
73251
73391
  const generics = this.genericEnvironment(schemaClass2, receiver);
73252
73392
  const own = schemaClass2.members.find((member) => member.name === name);
73253
73393
  if (own !== void 0) {
73254
- return this.resolvedMember(own, generics, true);
73394
+ return this.resolvedMember(schemaClass2, own, generics);
73255
73395
  }
73256
73396
  for (const base of schemaClass2.baseTypes) {
73257
73397
  const parent = this.classesByName.get(base.name);
@@ -73264,7 +73404,7 @@ var init_dialogue_lower = __esm({
73264
73404
  return null;
73265
73405
  }
73266
73406
  interfaceMember(schemaInterface, name, visited) {
73267
- const interfaceId = schemaIdentityId(
73407
+ const interfaceId = materializedId(
73268
73408
  schemaInterface,
73269
73409
  "interface",
73270
73410
  schemaInterface.name
@@ -73272,7 +73412,8 @@ var init_dialogue_lower = __esm({
73272
73412
  if (visited.has(interfaceId)) return null;
73273
73413
  visited.add(interfaceId);
73274
73414
  const own = schemaInterface.members.find((member) => member.name === name);
73275
- if (own !== void 0) return this.resolvedMember(own, /* @__PURE__ */ new Map(), false);
73415
+ if (own !== void 0)
73416
+ return this.resolvedMember(schemaInterface, own, /* @__PURE__ */ new Map());
73276
73417
  for (const base of schemaInterface.baseTypes) {
73277
73418
  const parent = this.interfacesByName.get(base.name);
73278
73419
  if (parent === void 0) continue;
@@ -73281,13 +73422,13 @@ var init_dialogue_lower = __esm({
73281
73422
  }
73282
73423
  return null;
73283
73424
  }
73284
- resolvedMember(member, generics, classPropertyIsGetter) {
73425
+ resolvedMember(owner, member, generics) {
73285
73426
  if (member.kind === "function") return null;
73286
73427
  const typeInfo = this.typeInfoForManifestType(member.type, generics);
73287
73428
  if (typeInfo === null) return null;
73288
73429
  return {
73289
- kind: classPropertyIsGetter && member.kind === "property" ? "getter" : "stored",
73290
- memberId: schemaIdentityId(member, "member", member.name),
73430
+ kind: owner.kind === "class" && member.kind === "property" ? "getter" : "stored",
73431
+ memberId: materializedMemberId(owner, member),
73291
73432
  schemaKey: member.name,
73292
73433
  typeInfo
73293
73434
  };
@@ -73295,7 +73436,7 @@ var init_dialogue_lower = __esm({
73295
73436
  genericEnvironment(schemaClass2, receiver) {
73296
73437
  const result = /* @__PURE__ */ new Map();
73297
73438
  for (const parameter4 of schemaClass2.genericParameters) {
73298
- const parameterId = schemaIdentityId(
73439
+ const parameterId = materializedId(
73299
73440
  parameter4.identity,
73300
73441
  "generic-parameter",
73301
73442
  `${schemaClass2.name}.${parameter4.name}`
@@ -73348,12 +73489,12 @@ var init_dialogue_lower = __esm({
73348
73489
  type: type.name === "Dictionary" ? 5 /* Dictionary */ : 6 /* List */,
73349
73490
  required: required2,
73350
73491
  entryTypeInfo,
73351
- ...keyEnum === void 0 ? {} : { keyEnumId: schemaIdentityId(keyEnum, "enum", keyEnum.name) }
73492
+ ...keyEnum === void 0 ? {} : { keyEnumId: materializedId(keyEnum, "enum", keyEnum.name) }
73352
73493
  };
73353
73494
  }
73354
73495
  const schemaClass2 = this.classesByName.get(type.name);
73355
73496
  if (schemaClass2 !== void 0) {
73356
- const classId = schemaIdentityId(schemaClass2, "class", schemaClass2.name);
73497
+ const classId = materializedId(schemaClass2, "class", schemaClass2.name);
73357
73498
  const typeArguments = Object.fromEntries(
73358
73499
  schemaClass2.genericParameters.flatMap((parameter4, index) => {
73359
73500
  const argument2 = type.arguments[index];
@@ -73362,7 +73503,7 @@ var init_dialogue_lower = __esm({
73362
73503
  if (argumentType === null) return [];
73363
73504
  return [
73364
73505
  [
73365
- schemaIdentityId(
73506
+ materializedId(
73366
73507
  parameter4.identity,
73367
73508
  "generic-parameter",
73368
73509
  `${schemaClass2.name}.${parameter4.name}`
@@ -73383,7 +73524,7 @@ var init_dialogue_lower = __esm({
73383
73524
  if (neoInterface !== void 0) {
73384
73525
  return {
73385
73526
  type: 22 /* Interface */,
73386
- interfaceId: schemaIdentityId(
73527
+ interfaceId: materializedId(
73387
73528
  neoInterface,
73388
73529
  "interface",
73389
73530
  neoInterface.name
@@ -73395,7 +73536,7 @@ var init_dialogue_lower = __esm({
73395
73536
  if (neoEnum !== void 0) {
73396
73537
  return {
73397
73538
  type: 8 /* Enum */,
73398
- enumId: schemaIdentityId(neoEnum, "enum", neoEnum.name),
73539
+ enumId: materializedId(neoEnum, "enum", neoEnum.name),
73399
73540
  required: required2
73400
73541
  };
73401
73542
  }
@@ -79585,6 +79726,151 @@ var init_world_grid_query = __esm({
79585
79726
  }
79586
79727
  });
79587
79728
 
79729
+ // ../src/runtime/neoscript/getter-subscriptions.ts
79730
+ function* readKeys(reads) {
79731
+ yield* reads.valueIds;
79732
+ yield* reads.containerIds;
79733
+ yield* reads.containerEntryReadIds;
79734
+ for (const id2 of reads.staticMemberIds) yield staticReadKey(id2);
79735
+ }
79736
+ var NeoScriptGetterSubscription, NO_SUBSCRIPTIONS, staticReadKey, NeoScriptGetterSubscriptions;
79737
+ var init_getter_subscriptions = __esm({
79738
+ "../src/runtime/neoscript/getter-subscriptions.ts"() {
79739
+ "use strict";
79740
+ init_initializer_context();
79741
+ NeoScriptGetterSubscription = class {
79742
+ constructor(rowId, getterId, classId) {
79743
+ this.rowId = rowId;
79744
+ this.getterId = getterId;
79745
+ this.classId = classId;
79746
+ }
79747
+ rowId;
79748
+ getterId;
79749
+ classId;
79750
+ targets = [];
79751
+ reads = new InitializerReadSet();
79752
+ queued = false;
79753
+ /** Queued only to arm: its first read calls no handler. */
79754
+ arming = true;
79755
+ /** Its handlers are running. */
79756
+ handling = false;
79757
+ };
79758
+ NO_SUBSCRIPTIONS = [];
79759
+ staticReadKey = (memberId) => `static:${memberId}`;
79760
+ NeoScriptGetterSubscriptions = class _NeoScriptGetterSubscriptions {
79761
+ byRow = /* @__PURE__ */ new Map();
79762
+ byRead = /* @__PURE__ */ new Map();
79763
+ /** Reads no row id names, such as corpus or world queries, hear every write. */
79764
+ readingEverything = /* @__PURE__ */ new Set();
79765
+ pending = [];
79766
+ get empty() {
79767
+ return this.byRow.size === 0;
79768
+ }
79769
+ get(rowId, getterId) {
79770
+ return this.byRow.get(rowId)?.get(getterId);
79771
+ }
79772
+ add(subscription) {
79773
+ this.register(subscription);
79774
+ this.queue(subscription);
79775
+ }
79776
+ drop(subscription) {
79777
+ const getters = this.byRow.get(subscription.rowId);
79778
+ if (getters?.get(subscription.getterId) !== subscription) return;
79779
+ getters.delete(subscription.getterId);
79780
+ if (getters.size === 0) this.byRow.delete(subscription.rowId);
79781
+ this.unindex(subscription);
79782
+ }
79783
+ noteValueWrite(rowId) {
79784
+ this.noteRead(rowId);
79785
+ }
79786
+ noteStaticBinding(memberId) {
79787
+ this.noteRead(staticReadKey(memberId));
79788
+ }
79789
+ /** Replace what the subscription's last read touched. */
79790
+ setReads(subscription, reads) {
79791
+ this.unindex(subscription);
79792
+ subscription.reads = reads;
79793
+ if (this.get(subscription.rowId, subscription.getterId) !== subscription)
79794
+ return;
79795
+ if (reads.corpusReadReasons.size > 0 || reads.worldQueries.size > 0)
79796
+ this.readingEverything.add(subscription);
79797
+ for (const key of readKeys(reads)) {
79798
+ let subscriptions = this.byRead.get(key);
79799
+ if (subscriptions === void 0) {
79800
+ subscriptions = /* @__PURE__ */ new Set();
79801
+ this.byRead.set(key, subscriptions);
79802
+ }
79803
+ subscriptions.add(subscription);
79804
+ }
79805
+ }
79806
+ takePending() {
79807
+ if (this.pending.length === 0) return NO_SUBSCRIPTIONS;
79808
+ const pending = this.pending;
79809
+ this.pending = [];
79810
+ return pending;
79811
+ }
79812
+ /** A throw skipped `drained` from `next` on. They stay queued, so none goes deaf. */
79813
+ requeue(drained, next) {
79814
+ for (let index = next; index < drained.length; index += 1)
79815
+ this.pending.push(drained[index]);
79816
+ }
79817
+ /** A copy for a fresh runtime over the same rows, armed exactly as these are. */
79818
+ clone() {
79819
+ const cloned = new _NeoScriptGetterSubscriptions();
79820
+ const copies = /* @__PURE__ */ new Map();
79821
+ for (const getters of this.byRow.values()) {
79822
+ for (const source of getters.values()) {
79823
+ const copy = new NeoScriptGetterSubscription(
79824
+ source.rowId,
79825
+ source.getterId,
79826
+ source.classId
79827
+ );
79828
+ copy.targets.push(...source.targets);
79829
+ copy.arming = source.arming;
79830
+ copies.set(source, copy);
79831
+ cloned.register(copy);
79832
+ cloned.setReads(copy, source.reads);
79833
+ }
79834
+ }
79835
+ for (const source of this.pending) {
79836
+ const copy = copies.get(source);
79837
+ if (copy !== void 0) cloned.queue(copy);
79838
+ }
79839
+ return cloned;
79840
+ }
79841
+ register(subscription) {
79842
+ let getters = this.byRow.get(subscription.rowId);
79843
+ if (getters === void 0) {
79844
+ getters = /* @__PURE__ */ new Map();
79845
+ this.byRow.set(subscription.rowId, getters);
79846
+ }
79847
+ getters.set(subscription.getterId, subscription);
79848
+ }
79849
+ queue(subscription) {
79850
+ if (subscription.queued) return;
79851
+ subscription.queued = true;
79852
+ this.pending.push(subscription);
79853
+ }
79854
+ noteRead(key) {
79855
+ const subscriptions = this.byRead.get(key);
79856
+ if (subscriptions !== void 0)
79857
+ for (const subscription of subscriptions) this.queue(subscription);
79858
+ for (const subscription of this.readingEverything)
79859
+ if (!subscription.handling) this.queue(subscription);
79860
+ }
79861
+ unindex(subscription) {
79862
+ this.readingEverything.delete(subscription);
79863
+ for (const key of readKeys(subscription.reads)) {
79864
+ const subscriptions = this.byRead.get(key);
79865
+ if (subscriptions === void 0) continue;
79866
+ subscriptions.delete(subscription);
79867
+ if (subscriptions.size === 0) this.byRead.delete(key);
79868
+ }
79869
+ }
79870
+ };
79871
+ }
79872
+ });
79873
+
79588
79874
  // ../src/models/decimal/decimal-math.ts
79589
79875
  function pow10(exponent) {
79590
79876
  return TEN ** BigInt(exponent);
@@ -80082,6 +80368,7 @@ function evalMemberById(vm, id2) {
80082
80368
  return vm.members.find((a) => a.id === id2) ?? null;
80083
80369
  }
80084
80370
  function evalValueById(ctx, id2, runtimeValues, overlayValues) {
80371
+ ctx.__getterReads?.add(id2);
80085
80372
  if (ctx.__removedValueIds?.has(id2)) {
80086
80373
  ctx.valueDependencies?.add(id2);
80087
80374
  return null;
@@ -80754,8 +81041,13 @@ function bindThisValue(ctx, thisValue) {
80754
81041
  const rowId = thisValue === ctx.thisValue ? ctx.thisValueRowId : trackedRowForValueReference(thisValue, ctx)?.id;
80755
81042
  return { ...ctx, thisValue, thisValueRowId: rowId };
80756
81043
  }
81044
+ function observeValueWrite(ctx, row) {
81045
+ ctx.beforeValueWrite?.(row);
81046
+ ctx.__getterSubscriptions?.noteValueWrite(row.id);
81047
+ }
80757
81048
  function assertStoredReplayMutableValue(value, ctx, label, key) {
80758
- if (ctx.storedConstructionReplay !== true && !ctx.assertValueWritable && !ctx.beforeValueWrite)
81049
+ const observesWrites = ctx.beforeValueWrite !== void 0 || ctx.__getterSubscriptions?.empty === false;
81050
+ if (ctx.storedConstructionReplay !== true && !ctx.assertValueWritable && !observesWrites)
80759
81051
  return;
80760
81052
  let rowId = findKnownRowIdByValueReference(value, ctx);
80761
81053
  if (rowId === null) return;
@@ -80774,7 +81066,7 @@ function assertStoredReplayMutableValue(value, ctx, label, key) {
80774
81066
  );
80775
81067
  if (ctx.__runtimeSessionValues?.has(rowId) !== true)
80776
81068
  ctx.assertValueWritable?.(rowId);
80777
- if (row) ctx.beforeValueWrite?.(row);
81069
+ if (row) observeValueWrite(ctx, row);
80778
81070
  if (ctx.__runtimeSessionValues?.has(rowId) === true) return;
80779
81071
  if (ctx.storedConstructionReplay !== true) return;
80780
81072
  throw new NSGetterRuntimeError(
@@ -81357,10 +81649,14 @@ function inspectNeoScriptValueReference(ctx, value, collection, index) {
81357
81649
  const row = readNeoScriptRuntimeRow(ctx, id2);
81358
81650
  return row === null ? null : { row, member: memberForValueRow(row, ctx) };
81359
81651
  }
81652
+ function getterSubscriptionsFor(ctx) {
81653
+ return ctx.getterDispatch === "afterEntryStatement" /* AfterEntryStatement */ ? new NeoScriptGetterSubscriptions() : void 0;
81654
+ }
81360
81655
  function withEvaluationRuntime(ctx, writes) {
81361
81656
  if (ctx.__runtimeSessionValues !== void 0 && ctx.__executionState !== void 0 && ctx.__valueOverlay !== void 0) {
81362
81657
  ctx.__unorderedListProjections ??= createUnorderedListProjectionCache();
81363
81658
  ctx.__runtimeClassOverrides ??= /* @__PURE__ */ new Map();
81659
+ ctx.__getterSubscriptions ??= getterSubscriptionsFor(ctx);
81364
81660
  return ctx;
81365
81661
  }
81366
81662
  const overlay = ctx.__valueOverlay ?? (ctx.storedConstructionReplay === true ? /* @__PURE__ */ new Map() : buildValueOverlay(ctx.vm));
@@ -81385,6 +81681,7 @@ function withEvaluationRuntime(ctx, writes) {
81385
81681
  trigger: remap(ctx.dialogueContext.trigger)
81386
81682
  },
81387
81683
  __runtimeSessionValues: runtimeSessionValues,
81684
+ __getterSubscriptions: ctx.__getterSubscriptions ?? getterSubscriptionsFor(ctx),
81388
81685
  __copiedChangeListeners: ctx.__copiedChangeListeners ?? /* @__PURE__ */ new Map(),
81389
81686
  __runtimeClassOverrides: ctx.__runtimeClassOverrides ?? /* @__PURE__ */ new Map(),
81390
81687
  __saveStaticBindings: ctx.__saveStaticBindings ?? staticBindingMap(ctx.saveStaticBindings),
@@ -82275,6 +82572,7 @@ function validateTryInstruction(instruction) {
82275
82572
  function evalBody(body, scope, ctx, options, preparedFrame) {
82276
82573
  const state = ctx.__executionState;
82277
82574
  if (!state) return evalInstructions(body.instructions, scope, ctx, options);
82575
+ const hostBody = state.frames.length === 0 && ctx.__getterSubscriptions !== void 0;
82278
82576
  state.frames.push(
82279
82577
  preparedFrame ?? {
82280
82578
  name: body.source?.name ?? "<body>",
@@ -82283,7 +82581,14 @@ function evalBody(body, scope, ctx, options, preparedFrame) {
82283
82581
  }
82284
82582
  );
82285
82583
  try {
82286
- return evalInstructions(body.instructions, scope, ctx, options);
82584
+ if (!hostBody)
82585
+ return evalInstructions(body.instructions, scope, ctx, options);
82586
+ const result = evalInstructions(body.instructions, scope, ctx, {
82587
+ ...options,
82588
+ dispatchesGetterChanges: true
82589
+ });
82590
+ dispatchGetterSubscriptions(ctx);
82591
+ return result;
82287
82592
  } catch (error) {
82288
82593
  attachNeoScriptTrace(error, state);
82289
82594
  throw error;
@@ -82662,6 +82967,8 @@ function evalInstructions(instructions, scope, ctx, options) {
82662
82967
  return { kind: "continue" };
82663
82968
  }
82664
82969
  void touchUnknown(ins);
82970
+ if (options.dispatchesGetterChanges === true)
82971
+ dispatchGetterSubscriptions(ctx);
82665
82972
  } catch (error) {
82666
82973
  positionNeoScriptFrame(ctx, ins.source);
82667
82974
  if (ctx.__executionState)
@@ -82745,6 +83052,30 @@ function resolveNeoScriptChangeListenerSlot(ownerId, memberId, ctx) {
82745
83052
  typeInfo: changeListenerObservedType(raw, member, env, ctx)
82746
83053
  };
82747
83054
  }
83055
+ function resolveNeoScriptGetterListenerSlot(ownerId, memberId, ctx) {
83056
+ const owner = evalValueById(
83057
+ ctx,
83058
+ ownerId,
83059
+ ctx.__runtimeSessionValues,
83060
+ ctx.__valueOverlay
83061
+ );
83062
+ if (owner === null || owner.value === null) return null;
83063
+ const entry = changeListenerMemberOnOwner(owner, memberId, ctx);
83064
+ if (entry === void 0) return null;
83065
+ const member = evalMemberById(ctx.vm, entry.memberId);
83066
+ if (member === null || !isMemberNSProperty(member)) return null;
83067
+ if (isStaticMember(member)) return null;
83068
+ const env = runtimeGenericEnvForValueRow(owner, ctx, /* @__PURE__ */ new Set());
83069
+ const typeInfo = substituteRuntimeTypeInfo(member.returnTypeInfo, env, ctx);
83070
+ if (typeInfo.type === NS_TYPE_VOID) return null;
83071
+ return {
83072
+ owner,
83073
+ member,
83074
+ memberId: evaluatorResolutionCache(ctx).canonicalMemberId(member.id),
83075
+ schemaKey: entry.schemaKey,
83076
+ typeInfo
83077
+ };
83078
+ }
82748
83079
  function changeListenerObservedType(raw, member, env, ctx) {
82749
83080
  const runtime = runtimeTypeInfoForMember(raw, env, ctx, /* @__PURE__ */ new Set());
82750
83081
  if (!isMemberLookup(member)) return runtime;
@@ -82767,6 +83098,18 @@ function changeListenerObservedType(raw, member, env, ctx) {
82767
83098
  required: isRequiredMember(member)
82768
83099
  };
82769
83100
  }
83101
+ function readNeoScriptChangeListenerValue(slot, ctx) {
83102
+ const result = dispatchSchemaMember(
83103
+ readRowValue(slot.owner, ctx),
83104
+ slot.schemaKey,
83105
+ ctx
83106
+ );
83107
+ if (result.kind !== "ok")
83108
+ throw new NSGetterRuntimeError(
83109
+ `OnChanged cannot read '${slot.owner.id}/${slot.memberId}'.`
83110
+ );
83111
+ return result.value;
83112
+ }
82770
83113
  function resolveNeoScriptChangeListenerHandler(slot, target, ctx) {
82771
83114
  const raw = evalMemberById(ctx.vm, target.memberId);
82772
83115
  if (raw === null) return null;
@@ -82828,14 +83171,16 @@ function evalChangeListenerInstruction(ins, scope, ctx, options) {
82828
83171
  throw new NSGetterRuntimeError(
82829
83172
  "OnChanged requires an existing logical owner."
82830
83173
  );
82831
- const slot = resolveNeoScriptChangeListenerSlot(
83174
+ const stored = resolveNeoScriptChangeListenerSlot(
82832
83175
  owner.id,
82833
83176
  ins.target.memberId,
82834
83177
  ctx
82835
83178
  );
83179
+ const getter = stored === null ? resolveNeoScriptGetterListenerSlot(owner.id, ins.target.memberId, ctx) : null;
83180
+ const slot = stored ?? getter;
82836
83181
  if (slot === null)
82837
83182
  throw new NSGetterRuntimeError(
82838
- `OnChanged member '${ins.target.memberId}' is not an observable stored member of owner '${owner.id}'.`
83183
+ `OnChanged member '${ins.target.memberId}' is not an observable member or getter of owner '${owner.id}'.`
82839
83184
  );
82840
83185
  const listener = evalPointer(ins.listener, scope, ctx);
82841
83186
  if (!isMemberDelegateTarget(listener))
@@ -82847,6 +83192,15 @@ function evalChangeListenerInstruction(ins, scope, ctx, options) {
82847
83192
  throw new NSGetterRuntimeError(
82848
83193
  `OnChanged handler '${listener.memberId}' cannot accept the value of '${slot.member.name}' on '${owner.id}'.`
82849
83194
  );
83195
+ if (getter !== null) {
83196
+ editGetterSubscription(
83197
+ getter,
83198
+ handler.target,
83199
+ ins.type === "addChangeListener" /* addChangeListener */,
83200
+ ctx
83201
+ );
83202
+ return;
83203
+ }
82850
83204
  options.writes.push({
82851
83205
  kind: "changeListener",
82852
83206
  target: ins.target,
@@ -82856,6 +83210,95 @@ function evalChangeListenerInstruction(ins, scope, ctx, options) {
82856
83210
  listener: handler.target
82857
83211
  });
82858
83212
  }
83213
+ function editGetterSubscription(slot, target, add, ctx) {
83214
+ if ((ctx.__executionState?.constructionStack.length ?? 0) > 0)
83215
+ throw new NSGetterRuntimeError(
83216
+ "A getter subscription lives only for the play session, so a constructor can't make one. Subscribe from a function."
83217
+ );
83218
+ const subscriptions = ctx.__getterSubscriptions;
83219
+ if (subscriptions === void 0) return;
83220
+ let subscription = subscriptions.get(slot.owner.id, slot.memberId);
83221
+ const found = subscription?.targets.findIndex(
83222
+ (existing) => existing.memberId === target.memberId && existing.valueId === target.valueId
83223
+ ) ?? -1;
83224
+ if (!add) {
83225
+ if (subscription === void 0 || found < 0) return;
83226
+ subscription.targets.splice(found, 1);
83227
+ if (subscription.targets.length === 0) subscriptions.drop(subscription);
83228
+ return;
83229
+ }
83230
+ if (found >= 0) return;
83231
+ if (subscription === void 0) {
83232
+ subscription = new NeoScriptGetterSubscription(
83233
+ slot.owner.id,
83234
+ slot.memberId,
83235
+ slot.owner.classId
83236
+ );
83237
+ subscriptions.add(subscription);
83238
+ }
83239
+ subscription.targets.push(target);
83240
+ }
83241
+ function dispatchGetterSubscriptions(ctx) {
83242
+ const subscriptions = ctx.__getterSubscriptions;
83243
+ if (subscriptions === void 0) return;
83244
+ const draining = subscriptions.takePending();
83245
+ let next = 0;
83246
+ try {
83247
+ while (next < draining.length)
83248
+ dispatchGetterSubscription(draining[next++], subscriptions, ctx);
83249
+ } finally {
83250
+ subscriptions.requeue(draining, next);
83251
+ }
83252
+ }
83253
+ function dispatchGetterSubscription(subscription, subscriptions, ctx) {
83254
+ subscription.queued = false;
83255
+ const notify = !subscription.arming;
83256
+ subscription.arming = false;
83257
+ if (subscription.targets.length === 0) return;
83258
+ const slot = resolveNeoScriptGetterListenerSlot(
83259
+ subscription.rowId,
83260
+ subscription.getterId,
83261
+ ctx
83262
+ );
83263
+ if (slot === null || slot.owner.classId !== subscription.classId) {
83264
+ subscriptions.drop(subscription);
83265
+ return;
83266
+ }
83267
+ const reads = new InitializerReadSet();
83268
+ let value;
83269
+ try {
83270
+ value = readNeoScriptChangeListenerValue(slot, {
83271
+ ...ctx,
83272
+ __getterReads: reads.valueIds,
83273
+ containerDependencies: reads.containerIds,
83274
+ staticMemberDependencies: reads.staticMemberIds,
83275
+ worldQueryDependencies: reads.worldQueries,
83276
+ onUnattributableValueRead: reads.recordUnattributableValueRead
83277
+ });
83278
+ } finally {
83279
+ subscriptions.setReads(subscription, reads);
83280
+ }
83281
+ if (!notify) return;
83282
+ const owner = readRowValue(slot.owner, ctx);
83283
+ subscription.handling = true;
83284
+ try {
83285
+ for (const target of [...subscription.targets]) {
83286
+ const handler = resolveNeoScriptChangeListenerHandler(slot, target, ctx);
83287
+ if (handler === null) continue;
83288
+ invokeDelegateValue(
83289
+ handler.invocation,
83290
+ [value],
83291
+ ctx,
83292
+ void 0,
83293
+ owner,
83294
+ "discarded",
83295
+ () => dispatchGetterSubscriptions(ctx)
83296
+ );
83297
+ }
83298
+ } finally {
83299
+ subscription.handling = false;
83300
+ }
83301
+ }
82859
83302
  function evalActionListenerInstruction(ins, scope, ctx, options) {
82860
83303
  const operator = ins.type === "addActionListener" /* addActionListener */ ? "+=" : "-=";
82861
83304
  if (ins.target.pointer.type === "variable" /* variable */) {
@@ -83032,6 +83475,7 @@ function applyOverlayStaticAssignment(memberId, value, ctx) {
83032
83475
  );
83033
83476
  }
83034
83477
  const bindings = writableStaticBindingMap(member, ctx);
83478
+ ctx.__getterSubscriptions?.noteStaticBinding(member.id);
83035
83479
  if (value === null || value === void 0) {
83036
83480
  if (isRequiredMember(member)) {
83037
83481
  throw new NSGetterRuntimeError(
@@ -83070,7 +83514,7 @@ function applyOverlayStaticAssignment(memberId, value, ctx) {
83070
83514
  `Static member '${member.name}' is bound to missing value '${currentId}'.`
83071
83515
  );
83072
83516
  }
83073
- ctx.beforeValueWrite?.(row2);
83517
+ observeValueWrite(ctx, row2);
83074
83518
  row2.value = value;
83075
83519
  row2.updatedAt = Date.now();
83076
83520
  invalidateEvaluatorIndexes(ctx);
@@ -89871,14 +90315,14 @@ function evalVariantApply(info, scope, ctx) {
89871
90315
  });
89872
90316
  for (const valueId of resolution.clearedValueIds) {
89873
90317
  const previous = readNeoScriptRuntimeRow(ctx, valueId);
89874
- if (previous) ctx.beforeValueWrite?.(previous);
90318
+ if (previous) observeValueWrite(ctx, previous);
89875
90319
  deleteRuntimeSessionValue(ctx, valueId);
89876
90320
  ctx.__valueOverlay?.delete(valueId);
89877
90321
  }
89878
90322
  for (const row of resolution.rows) {
89879
90323
  if (row.id === rootRow.id) continue;
89880
90324
  const previous = readNeoScriptRuntimeRow(ctx, row.id);
89881
- if (previous) ctx.beforeValueWrite?.(previous);
90325
+ if (previous) observeValueWrite(ctx, previous);
89882
90326
  ctx.__valueOverlay?.set(row.id, deepClonePlainData(row));
89883
90327
  }
89884
90328
  if (typeof receiver !== "object" || receiver === null || Array.isArray(receiver) || typeof resolution.root.value !== "object" || resolution.root.value === null || Array.isArray(resolution.root.value)) {
@@ -91340,6 +91784,8 @@ var init_evaluateNSGetter = __esm({
91340
91784
  init_cell_pattern();
91341
91785
  init_cell_pattern2();
91342
91786
  init_world_grid_query();
91787
+ init_initializer_context();
91788
+ init_getter_subscriptions();
91343
91789
  init_classes();
91344
91790
  init_core();
91345
91791
  init_construction_requirements();
@@ -106560,6 +107006,67 @@ var init_project_commit_preparation_scoped_loader = __esm({
106560
107006
  }
106561
107007
  });
106562
107008
 
107009
+ // ../src/database/lookup-target-class-contract.ts
107010
+ function lookupTargetClassContractViolation(args) {
107011
+ const actualClassId = typeof args.targetClassId === "string" ? args.targetClassId : null;
107012
+ if (actualClassId === null) return null;
107013
+ const classById = args.schemaIndex.classesById;
107014
+ if (!classById.has(actualClassId)) return null;
107015
+ const entryClassId = lookupEntryClassId(
107016
+ args.member,
107017
+ args.schemaIndex.membersById
107018
+ );
107019
+ if (entryClassId !== null && classById.has(entryClassId) && !args.schemaIndex.classIsOrExtends(actualClassId, entryClassId)) {
107020
+ return `lookup member ${args.member.name} target ${args.targetValueId} is ${classDisplayName(actualClassId, classById)}, but collection ${collectionMemberName(args.member, args.schemaIndex)} holds ${classDisplayName(entryClassId, classById)}.`;
107021
+ }
107022
+ const declaredClassId = lookupDeclaredClassId(args.member, classById);
107023
+ if (declaredClassId !== null && !args.schemaIndex.classIsOrExtends(actualClassId, declaredClassId)) {
107024
+ const narrowedFrom = entryClassId === null || !classById.has(entryClassId) ? `the collection ${collectionMemberName(args.member, args.schemaIndex)}` : `the collection ${collectionMemberName(args.member, args.schemaIndex)} holds ${classDisplayName(entryClassId, classById)}`;
107025
+ return `lookup member ${args.member.name} target ${args.targetValueId} is ${classDisplayName(actualClassId, classById)}, but the member declares ${classDisplayName(declaredClassId, classById)} \u2014 ${narrowedFrom} and this member narrows it. Point it at a ${classDisplayName(declaredClassId, classById)} row.`;
107026
+ }
107027
+ return null;
107028
+ }
107029
+ function lookupEntryClassId(member, memberById2) {
107030
+ const collection = memberById2.get(member.collectionMemberId);
107031
+ if (collection === void 0) return null;
107032
+ if (!isMemberListBase(collection) && !isMemberDictionaryBase(collection)) {
107033
+ return null;
107034
+ }
107035
+ const entry = memberById2.get(collection.entryMemberId);
107036
+ if (entry === void 0) return null;
107037
+ return isMemberClassBase(entry) ? entry.classId : null;
107038
+ }
107039
+ function lookupDeclaredClassId(member, classById) {
107040
+ const declared = member.declaredTypeInfo;
107041
+ if (declared === void 0) return null;
107042
+ if (declared === null) return null;
107043
+ if (declared.type !== 7 /* Class */) return null;
107044
+ return classById.has(declared.classId) ? declared.classId : null;
107045
+ }
107046
+ function classDisplayName(classId, classById) {
107047
+ return classById.get(classId)?.name ?? classId;
107048
+ }
107049
+ function collectionMemberName(member, schemaIndex) {
107050
+ const collection = schemaIndex.membersById.get(member.collectionMemberId);
107051
+ if (collection === void 0) return member.collectionMemberId;
107052
+ const owner = schemaIndex.schemaPlacementsByMemberId.get(collection.id)?.[0]?.ownerClass;
107053
+ return owner ? `${owner.name}.${collection.name}` : collection.name;
107054
+ }
107055
+ function lookupDeclaredClassContractViolation(member, schemaIndex) {
107056
+ const declared = lookupDeclaredClassId(member, schemaIndex.classesById);
107057
+ const entry = lookupEntryClassId(member, schemaIndex.membersById);
107058
+ if (declared === null || entry === null || !schemaIndex.classesById.has(entry))
107059
+ return null;
107060
+ if (schemaIndex.classIsOrExtends(declared, entry)) return null;
107061
+ return `Lookup member ${member.name} declares ${classDisplayName(declared, schemaIndex.classesById)}, but collection ${collectionMemberName(member, schemaIndex)} holds ${classDisplayName(entry, schemaIndex.classesById)}.`;
107062
+ }
107063
+ var init_lookup_target_class_contract = __esm({
107064
+ "../src/database/lookup-target-class-contract.ts"() {
107065
+ "use strict";
107066
+ init_member_kinds();
107067
+ }
107068
+ });
107069
+
106563
107070
  // ../src/models/dialogue-context-resolver.ts
106564
107071
  function resolveDialoguePrimaryValueId(dialogue) {
106565
107072
  return dialogue.primaryLinkedValueId ?? null;
@@ -107175,6 +107682,7 @@ var init_neoscript2 = __esm({
107175
107682
  init_evaluateNSGetter();
107176
107683
  init_evaluateInitializer();
107177
107684
  init_initializer_context();
107685
+ init_getter_subscriptions();
107178
107686
  }
107179
107687
  });
107180
107688
 
@@ -110127,76 +110635,6 @@ var init_project_document_value_overlay = __esm({
110127
110635
  }
110128
110636
  });
110129
110637
 
110130
- // ../src/database/lookup-target-class-contract.ts
110131
- function lookupTargetClassContractViolation(args) {
110132
- const actualClassId = typeof args.targetClassId === "string" ? args.targetClassId : null;
110133
- if (actualClassId === null) return null;
110134
- const classById = new Map(
110135
- args.classes.map((schemaClass2) => [schemaClass2.id, schemaClass2])
110136
- );
110137
- if (!classById.has(actualClassId)) return null;
110138
- const entryClassId = lookupEntryClassId(args.member, args.memberById);
110139
- const collectionName = collectionMemberName(
110140
- args.member,
110141
- args.memberById,
110142
- args.classes
110143
- );
110144
- if (entryClassId !== null && classById.has(entryClassId) && !classAssignableToClass(actualClassId, entryClassId, classById)) {
110145
- return `lookup member ${args.member.name} target ${args.targetValueId} is ${classDisplayName(actualClassId, classById)}, but collection ${collectionName} holds ${classDisplayName(entryClassId, classById)}.`;
110146
- }
110147
- const declaredClassId = lookupDeclaredClassId(args.member, classById);
110148
- if (declaredClassId !== null && !classAssignableToClass(actualClassId, declaredClassId, classById)) {
110149
- const narrowedFrom = entryClassId === null || !classById.has(entryClassId) ? `the collection ${collectionName}` : `the collection ${collectionName} holds ${classDisplayName(entryClassId, classById)}`;
110150
- return `lookup member ${args.member.name} target ${args.targetValueId} is ${classDisplayName(actualClassId, classById)}, but the member declares ${classDisplayName(declaredClassId, classById)} \u2014 ${narrowedFrom} and this member narrows it. Point it at a ${classDisplayName(declaredClassId, classById)} row.`;
110151
- }
110152
- return null;
110153
- }
110154
- function lookupEntryClassId(member, memberById2) {
110155
- const collection = memberById2.get(member.collectionMemberId);
110156
- if (collection === void 0) return null;
110157
- if (!isMemberListBase(collection) && !isMemberDictionaryBase(collection)) {
110158
- return null;
110159
- }
110160
- const entry = memberById2.get(collection.entryMemberId);
110161
- if (entry === void 0) return null;
110162
- return isMemberClassBase(entry) ? entry.classId : null;
110163
- }
110164
- function lookupDeclaredClassId(member, classById) {
110165
- const declared = member.declaredTypeInfo;
110166
- if (declared === void 0) return null;
110167
- if (declared === null) return null;
110168
- if (declared.type !== 7 /* Class */) return null;
110169
- return classById.has(declared.classId) ? declared.classId : null;
110170
- }
110171
- function classAssignableToClass(actualClassId, expectedClassId, classById) {
110172
- const visited = /* @__PURE__ */ new Set();
110173
- let current = actualClassId;
110174
- while (current !== void 0 && !visited.has(current)) {
110175
- if (current === expectedClassId) return true;
110176
- visited.add(current);
110177
- current = classById.get(current)?.extendsClassId;
110178
- }
110179
- return false;
110180
- }
110181
- function classDisplayName(classId, classById) {
110182
- return classById.get(classId)?.name ?? classId;
110183
- }
110184
- function collectionMemberName(member, memberById2, classes) {
110185
- const collection = memberById2.get(member.collectionMemberId);
110186
- if (collection === void 0) return member.collectionMemberId;
110187
- const owner = classes.find(
110188
- (schemaClass2) => Object.values(schemaClass2.schema).includes(member.collectionMemberId)
110189
- );
110190
- if (owner === void 0) return collection.name;
110191
- return `${owner.name}.${collection.name}`;
110192
- }
110193
- var init_lookup_target_class_contract = __esm({
110194
- "../src/database/lookup-target-class-contract.ts"() {
110195
- "use strict";
110196
- init_member_kinds();
110197
- }
110198
- });
110199
-
110200
110638
  // ../src/database/stored-value-graph-semantics.ts
110201
110639
  function inspectStoredValueNode(options) {
110202
110640
  try {
@@ -111024,8 +111462,7 @@ var init_project_version_static_value_writes = __esm({
111024
111462
  member: dependency.member,
111025
111463
  targetValueId: targetId,
111026
111464
  targetClassId: target.classId ?? null,
111027
- memberById: this.schemaIndex.membersById,
111028
- classes: this.schemaIndex.classes
111465
+ schemaIndex: this.schemaIndex
111029
111466
  });
111030
111467
  if (violation !== null) throw new Error(`${path} ${violation}`);
111031
111468
  }
@@ -114350,7 +114787,10 @@ function hasAuthoredNeoScriptBody(member) {
114350
114787
  function assertConstructedGraphReferencesResolve(args) {
114351
114788
  const sites = collectTouchedInitializerSites(args);
114352
114789
  if (sites.length === 0) return;
114353
- const valueIds = new Set(args.document.values.map((value) => value.id));
114790
+ const valueById = new Map(
114791
+ args.document.values.map((value) => [value.id, value])
114792
+ );
114793
+ const schemaIndex = projectSchemaIndexFor(args.document);
114354
114794
  for (const site of sites) {
114355
114795
  if ((site.init.compiled?.parameters.length ?? 2) > 2) continue;
114356
114796
  if (site.sourceValueId === void 0 && [
@@ -114374,7 +114814,8 @@ function assertConstructedGraphReferencesResolve(args) {
114374
114814
  site,
114375
114815
  produced,
114376
114816
  createdValues,
114377
- documentValueIds: valueIds
114817
+ documentValuesById: valueById,
114818
+ schemaIndex
114378
114819
  });
114379
114820
  }
114380
114821
  }
@@ -114438,7 +114879,6 @@ function collectTouchedInitializerSites(args) {
114438
114879
  return sites;
114439
114880
  }
114440
114881
  function assertProducedGraphLookupsResolve(args) {
114441
- if (args.createdValues.length === 0) return;
114442
114882
  const rootMember = args.produced.classId === null ? args.site.member : { ...args.site.member, classId: args.produced.classId };
114443
114883
  const graphValues = args.createdValues;
114444
114884
  const rootRow = {
@@ -114464,20 +114904,28 @@ function assertProducedGraphLookupsResolve(args) {
114464
114904
  new Set(args.createdValues.map((value) => value.id)),
114465
114905
  [{ member: rootMember, valueId: rootRow.id }]
114466
114906
  );
114467
- const resolvableIds = /* @__PURE__ */ new Set([
114468
- ...args.documentValueIds,
114469
- ...args.createdValues.map((value) => value.id)
114470
- ]);
114471
- for (const row of args.createdValues) {
114472
- const member = ownerByValueId.get(row.id);
114473
- if (member === void 0 || member.kind !== 9 /* Lookup */) continue;
114907
+ const createdById = new Map(graphValues.map((value) => [value.id, value]));
114908
+ createdById.set(rootRow.id, rootRow);
114909
+ for (const row of createdById.values()) {
114910
+ const member = row === rootRow ? rootMember : ownerByValueId.get(row.id);
114911
+ if (member === void 0 || !isMemberLookupBase(member)) continue;
114474
114912
  if (!Array.isArray(row.value)) continue;
114475
114913
  for (const targetId of row.value) {
114476
114914
  if (typeof targetId !== "string") continue;
114477
- if (resolvableIds.has(targetId)) continue;
114478
- throw new Error(
114479
- `${args.site.label} constructs "${member.name}" pointing at value "${targetId}", which the construction does not produce and this project version does not contain.`
114480
- );
114915
+ const target = createdById.get(targetId) ?? args.documentValuesById.get(targetId) ?? initializerEvaluatorLookups(args.document).valueById(targetId);
114916
+ if (target === void 0 || target === null) {
114917
+ throw new Error(
114918
+ `${args.site.label} constructs "${member.name}" pointing at value "${targetId}", which the construction does not produce and this project version does not contain.`
114919
+ );
114920
+ }
114921
+ const violation = lookupTargetClassContractViolation({
114922
+ member,
114923
+ targetValueId: targetId,
114924
+ targetClassId: target.classId,
114925
+ schemaIndex: args.schemaIndex
114926
+ });
114927
+ if (violation !== null)
114928
+ throw new Error(`${args.site.label} ${violation}`);
114481
114929
  }
114482
114930
  }
114483
114931
  }
@@ -115468,6 +115916,9 @@ var SERVER_COMPILATION_PROJECT_CACHE_LIMIT, serverCompilationProjectCache, READ_
115468
115916
  var init_project_version_schema_commit = __esm({
115469
115917
  "../src/database/project-version-schema-commit.ts"() {
115470
115918
  "use strict";
115919
+ init_member_kinds();
115920
+ init_project_schema_index();
115921
+ init_lookup_target_class_contract();
115471
115922
  init_dialogue_logic_compile_pure();
115472
115923
  init_neoscript_compiler_revision();
115473
115924
  init_neoscript_source_catalog();
@@ -121203,6 +121654,19 @@ function assertProjectVersionWholeGraphWritesValid(args) {
121203
121654
  assertStagedUnorderedContainmentValid(args.document, projected, changes);
121204
121655
  assertWorldFootprintEntryTypes(projected, changes);
121205
121656
  assertStagedSchemaIdentifiersValid(projected, changes);
121657
+ if (changes.some(
121658
+ (change) => change.recordKind === "member" || change.recordKind === "class"
121659
+ )) {
121660
+ const schemaIndex = projectSchemaIndexFor(projected);
121661
+ for (const member of projected.members) {
121662
+ if (!isMemberLookupBase(member)) continue;
121663
+ const violation = lookupDeclaredClassContractViolation(
121664
+ member,
121665
+ schemaIndex
121666
+ );
121667
+ if (violation !== null) throw new Error(violation);
121668
+ }
121669
+ }
121206
121670
  assertStagedActionListenersValid(projected, changes);
121207
121671
  assertStagedUserDelegateTargetsValid(projected, changes);
121208
121672
  assertAuthoredMemberChangeListenersValid(args.document, projected, changes);
@@ -122417,6 +122881,9 @@ var KNOWN_TRANSACTION_OPERATIONS, INTERFACE_VALIDATED_RECORD_KINDS, ACTION_LISTE
122417
122881
  var init_project_version_whole_graph_validation = __esm({
122418
122882
  "../src/database/project-version-whole-graph-validation.ts"() {
122419
122883
  "use strict";
122884
+ init_member_kinds();
122885
+ init_project_schema_index();
122886
+ init_lookup_target_class_contract();
122420
122887
  init_project_root_members();
122421
122888
  init_neoscript_source_catalog();
122422
122889
  init_canonical_json();
@@ -129665,7 +130132,7 @@ function constructedClass(context, member, expression, currentClassId, path) {
129665
130132
  return target;
129666
130133
  }
129667
130134
  function assertConstructedClassAssignable(context, named, member, path) {
129668
- if (classAssignableToClass2(context, named.id, member.classId)) return;
130135
+ if (classAssignableToClass(context, named.id, member.classId)) return;
129669
130136
  const declaredName = context.classes.get(member.classId)?.name ?? member.classId;
129670
130137
  throw new Error(
129671
130138
  `Class value ${path} constructs ${named.name}, which does not descend from the declared class ${declaredName}.`
@@ -130371,13 +130838,13 @@ function validateReferenceContract(context, member, target, ownerValueId) {
130371
130838
  const expectedTypeName = lookupEntryTypeName(context, member);
130372
130839
  const targetData = valueData(context, target.id);
130373
130840
  const actualClassId = targetData && typeof targetData.classId === "string" ? targetData.classId : null;
130374
- if (expectedClassId && actualClassId && !classAssignableToClass2(context, actualClassId, expectedClassId)) {
130841
+ if (expectedClassId && actualClassId && !classAssignableToClass(context, actualClassId, expectedClassId)) {
130375
130842
  throw new Error(
130376
130843
  `Lookup member ${member.name} target ${target.id} is ${classDisplayName2(context, actualClassId)}, but collection ${collectionMemberName2(context, member)} holds ${classDisplayName2(context, expectedClassId)}.`
130377
130844
  );
130378
130845
  }
130379
130846
  const declaredClassId = lookupDeclaredClassId2(context, member);
130380
- if (declaredClassId && actualClassId && !classAssignableToClass2(context, actualClassId, declaredClassId)) {
130847
+ if (declaredClassId && actualClassId && !classAssignableToClass(context, actualClassId, declaredClassId)) {
130381
130848
  const narrowedFrom = expectedClassId === null ? `the collection ${collectionMemberName2(context, member)}` : `the collection ${collectionMemberName2(context, member)} holds ${classDisplayName2(context, expectedClassId)}`;
130382
130849
  throw new Error(
130383
130850
  `Lookup member ${member.name} target ${target.id} is ${classDisplayName2(context, actualClassId)}, but the member declares ${classDisplayName2(context, declaredClassId)} \u2014 ${narrowedFrom} and this member narrows it. Point it at a ${classDisplayName2(context, declaredClassId)} row.`
@@ -130437,7 +130904,7 @@ function referenceTypeNameAssignable(context, actualName, expectedName) {
130437
130904
  const actualClass = context.classesByName.get(actualName);
130438
130905
  const expectedClass = context.classesByName.get(expectedName);
130439
130906
  if (actualClass && expectedClass) {
130440
- return classAssignableToClass2(context, actualClass.id, expectedClass.id);
130907
+ return classAssignableToClass(context, actualClass.id, expectedClass.id);
130441
130908
  }
130442
130909
  const expectedInterfaceId = context.interfaceIdsByName.get(expectedName);
130443
130910
  return actualClass && expectedInterfaceId ? classImplementsInterface2(
@@ -130721,7 +131188,7 @@ function lookupEntryClassId2(context, member) {
130721
131188
  const entry = collection?.kind === "list" || collection?.kind === "dictionary" ? context.members.get(collection.entryMemberId) : null;
130722
131189
  return entry?.kind === "class" ? entry.classId : null;
130723
131190
  }
130724
- function classAssignableToClass2(context, actualClassId, expectedClassId) {
131191
+ function classAssignableToClass(context, actualClassId, expectedClassId) {
130725
131192
  let current = actualClassId;
130726
131193
  const visited = /* @__PURE__ */ new Set();
130727
131194
  while (current && !visited.has(current)) {
@@ -135969,11 +136436,7 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
135969
136436
  analysis.schema.classes.flatMap(
135970
136437
  (ownerClass) => ownerClass.members.map(
135971
136438
  (member) => [
135972
- materializedId(
135973
- member,
135974
- "member",
135975
- `${ownerClass.name}.${member.name}`
135976
- ),
136439
+ materializedMemberId(ownerClass, member),
135977
136440
  { ownerClass, member }
135978
136441
  ]
135979
136442
  )
@@ -137676,6 +138139,7 @@ function emitProse(context, textId, rawVariables, nodeBindings, primaryAlias) {
137676
138139
  );
137677
138140
  const variables = isObjectRecord2(rawVariables) ? rawVariables : {};
137678
138141
  const pieces = [];
138142
+ let pointerResolver;
137679
138143
  let cursor = 0;
137680
138144
  const pattern = /\{\{neo-var:([^}]+)\}\}/g;
137681
138145
  for (let match = pattern.exec(text); match !== null; match = pattern.exec(text)) {
@@ -137687,12 +138151,25 @@ function emitProse(context, textId, rawVariables, nodeBindings, primaryAlias) {
137687
138151
  cursor = pattern.lastIndex;
137688
138152
  continue;
137689
138153
  }
137690
- const sourcePath = authoredVariablePath(
137691
- variable2.sourcePath,
137692
- nodeBindings,
137693
- context.bindings,
137694
- context.memberNames
137695
- );
138154
+ let sourcePath;
138155
+ if (isObjectRecord2(variable2.pointer) && variable2.pointer.type !== "value" && variable2.sourcePath.includes(".@getter:") || variable2.sourcePath.startsWith(`ref:${PENDING_ID_PREFIX}`) || variable2.sourcePath.startsWith(`linked:${PENDING_ID_PREFIX}`)) {
138156
+ pointerResolver ??= {
138157
+ primaryAlias,
138158
+ bindingNameByValueId: {
138159
+ get: (valueId) => (nodeBindings.get(valueId) ?? context.bindings.get(valueId))?.name
138160
+ },
138161
+ memberNameById: context.memberNames,
138162
+ staticMemberExpressionById: context.staticMemberExpressions,
138163
+ enumOptionNameById: context.enumOptionNames
138164
+ };
138165
+ sourcePath = pointerSource2(variable2.pointer, pointerResolver);
138166
+ } else {
138167
+ sourcePath = authoredVariablePath(
138168
+ variable2.sourcePath,
138169
+ nodeBindings,
138170
+ context.bindings
138171
+ );
138172
+ }
137696
138173
  if (sourcePath === null) {
137697
138174
  pieces.push(`{MissingVariable(${quote5(variableId)})}`);
137698
138175
  cursor = pattern.lastIndex;
@@ -137737,27 +138214,14 @@ function recoverableProse(context, textId, rawVariables, owner, nodeBindings, pr
137737
138214
  return { source: recoveryProse(reason), reason };
137738
138215
  }
137739
138216
  }
137740
- function authoredVariablePath(sourcePath, nodeBindings, dialogueBindings, memberNames) {
138217
+ function authoredVariablePath(sourcePath, nodeBindings, dialogueBindings) {
137741
138218
  const persisted = PERSISTED_VARIABLE_PATH.exec(sourcePath);
137742
138219
  const lexicalPath = persisted ? (() => {
137743
138220
  const valueId = persisted[1];
137744
138221
  const binding = nodeBindings.get(valueId) ?? dialogueBindings.get(valueId);
137745
138222
  return binding ? `${binding.name}${persisted[2] ?? ""}` : null;
137746
138223
  })() : sourcePath;
137747
- if (lexicalPath === null) return null;
137748
- let missingMember = false;
137749
- const authored = lexicalPath.replace(
137750
- /\.@getter:([^.]+)/gu,
137751
- (_match, memberId) => {
137752
- const memberName = memberNames.get(memberId);
137753
- if (memberName === void 0) {
137754
- missingMember = true;
137755
- return "";
137756
- }
137757
- return `.${memberName}`;
137758
- }
137759
- );
137760
- return missingMember ? null : authored;
138224
+ return lexicalPath;
137761
138225
  }
137762
138226
  function recoveryProse(reason) {
137763
138227
  return `"""
@@ -138123,6 +138587,8 @@ var PERSISTED_VARIABLE_PATH, DIALOGUE_SYSTEM_TYPE_NAMES, ValuePlacementIndex, Un
138123
138587
  var init_dialogue_sources = __esm({
138124
138588
  "src/project-source/dialogue-sources.ts"() {
138125
138589
  "use strict";
138590
+ init_pending_id();
138591
+ init_ui_action_source();
138126
138592
  init_generated_csharp_identifiers();
138127
138593
  init_dialogue_group_symbols();
138128
138594
  init_language_spec();
@@ -154509,7 +154975,7 @@ var init_project_transaction = __esm({
154509
154975
  }
154510
154976
  });
154511
154977
 
154512
- // node_modules/fflate/esm/index.mjs
154978
+ // ../node_modules/fflate/esm/index.mjs
154513
154979
  import { createRequire } from "module";
154514
154980
  function gzipSync(data, opts) {
154515
154981
  if (!opts)
@@ -154521,7 +154987,7 @@ function gzipSync(data, opts) {
154521
154987
  }
154522
154988
  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;
154523
154989
  var init_esm = __esm({
154524
- "node_modules/fflate/esm/index.mjs"() {
154990
+ "../node_modules/fflate/esm/index.mjs"() {
154525
154991
  require2 = createRequire("/");
154526
154992
  try {
154527
154993
  _a = require2("worker_threads"), Worker4 = _a.Worker, isMarkedAsUntransferable = _a.isMarkedAsUntransferable;
@@ -156666,7 +157132,7 @@ var init_neoscript_file_references = __esm({
156666
157132
  }
156667
157133
  });
156668
157134
 
156669
- // node_modules/@formatjs/icu-messageformat-parser/node_modules/@formatjs/icu-skeleton-parser/index.js
157135
+ // ../node_modules/@formatjs/icu-skeleton-parser/index.js
156670
157136
  function parseDateTimeSkeleton(skeleton) {
156671
157137
  const result = {};
156672
157138
  skeleton.replace(DATE_TIME_REGEX, (match) => {
@@ -157035,7 +157501,7 @@ function parseNumberSkeleton(tokens) {
157035
157501
  }
157036
157502
  var DATE_TIME_REGEX, WHITE_SPACE_REGEX, FRACTION_PRECISION_REGEX, SIGNIFICANT_PRECISION_REGEX, INTEGER_WIDTH_REGEX, CONCISE_INTEGER_WIDTH_REGEX;
157037
157503
  var init_icu_skeleton_parser = __esm({
157038
- "node_modules/@formatjs/icu-messageformat-parser/node_modules/@formatjs/icu-skeleton-parser/index.js"() {
157504
+ "../node_modules/@formatjs/icu-skeleton-parser/index.js"() {
157039
157505
  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;
157040
157506
  WHITE_SPACE_REGEX = /[\t-\r \x85\u200E\u200F\u2028\u2029]/i;
157041
157507
  FRACTION_PRECISION_REGEX = /^\.(?:(0+)(\*)?|(#+)|(0+)(#+))$/g;
@@ -157045,7 +157511,7 @@ var init_icu_skeleton_parser = __esm({
157045
157511
  }
157046
157512
  });
157047
157513
 
157048
- // node_modules/@formatjs/icu-messageformat-parser/index.js
157514
+ // ../node_modules/@formatjs/icu-messageformat-parser/index.js
157049
157515
  function isArgumentElement(el) {
157050
157516
  return el.type === 1;
157051
157517
  }
@@ -157213,7 +157679,7 @@ function parse3(message, opts = {}) {
157213
157679
  }
157214
157680
  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;
157215
157681
  var init_icu_messageformat_parser = __esm({
157216
- "node_modules/@formatjs/icu-messageformat-parser/index.js"() {
157682
+ "../node_modules/@formatjs/icu-messageformat-parser/index.js"() {
157217
157683
  init_icu_skeleton_parser();
157218
157684
  ErrorKind = /* @__PURE__ */ (function(ErrorKind2) {
157219
157685
  ErrorKind2[ErrorKind2["EXPECT_ARGUMENT_CLOSING_BRACE"] = 1] = "EXPECT_ARGUMENT_CLOSING_BRACE";
@@ -171706,7 +172172,7 @@ var init_project_unity_export_types = __esm({
171706
172172
  "../src/models/exports/project-unity-export-types.ts"() {
171707
172173
  "use strict";
171708
172174
  init_unity_codegen_revision();
171709
- PROJECT_UNITY_EXPORT_SCHEMA_VERSION = 39;
172175
+ PROJECT_UNITY_EXPORT_SCHEMA_VERSION = 40;
171710
172176
  }
171711
172177
  });
171712
172178
 
@@ -182903,7 +183369,8 @@ function makeEvaluatorContext(rawDocument, interceptor) {
182903
183369
  rootValue: base.rootValue,
182904
183370
  dialogueContext: null,
182905
183371
  callInterceptor: interceptor,
182906
- nativeFunctionErrorCheckBehavior: "strict"
183372
+ nativeFunctionErrorCheckBehavior: "strict",
183373
+ getterDispatch: "afterEntryStatement" /* AfterEntryStatement */
182907
183374
  });
182908
183375
  }
182909
183376
  function documentRecord(document) {
@@ -183777,6 +184244,7 @@ function copyMocks(source, target) {
183777
184244
  function cloneTestEnvironment(source) {
183778
184245
  const cloned = createTestEnvironment(snapshotEnvironmentDocument(source));
183779
184246
  copyMocks(source, cloned);
184247
+ cloned.context.__getterSubscriptions = source.context.__getterSubscriptions?.clone();
183780
184248
  return cloned;
183781
184249
  }
183782
184250
  function runCallback(environment, delegate, deadlineMs) {
@@ -191439,7 +191907,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
191439
191907
  async function main() {
191440
191908
  const args = parseArgs(process.argv.slice(2));
191441
191909
  if (args.command === "--version") {
191442
- console.log("0.73.1");
191910
+ console.log("0.74.0");
191443
191911
  return;
191444
191912
  }
191445
191913
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {