@neocompose/cli 0.71.4 → 0.72.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
@@ -43,10 +43,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
43
43
 
44
44
  // ../src/models/members/member-storage.ts
45
45
  function isMemberStorageKind(value) {
46
- return value === 0 /* Inherit */ || value === 1 /* Immutable */ || value === 2 /* Save */ || value === 3 /* Session */ || value === 4 /* Writable */;
46
+ return value === 0 /* Inherit */ || value === 1 /* Immutable */ || value === 2 /* Save */ || value === 3 /* Session */ || value === 4 /* Writable */ || value === 5 /* User */;
47
47
  }
48
48
  function isEffectiveMemberStorage(value) {
49
- return value === 1 /* Immutable */ || value === 2 /* Save */ || value === 3 /* Session */;
49
+ return value === 1 /* Immutable */ || value === 2 /* Save */ || value === 3 /* Session */ || value === 5 /* User */;
50
50
  }
51
51
  function isClassAllowedStorage(value) {
52
52
  if (!isMemberStorageKind(value)) return false;
@@ -57,11 +57,19 @@ function childEffectiveStorage(declared, parent) {
57
57
  case 0 /* Inherit */:
58
58
  return parent;
59
59
  case 4 /* Writable */:
60
- return parent === 2 /* Save */ ? 2 /* Save */ : 3 /* Session */;
60
+ return parent === 2 /* Save */ || parent === 5 /* User */ ? parent : 3 /* Session */;
61
61
  default:
62
62
  return declared;
63
63
  }
64
64
  }
65
+ function allowedStorageAdmits(constraint, storage) {
66
+ if (constraint !== 4 /* Writable */) return storage === constraint;
67
+ return storage === 2 /* Save */ || storage === 3 /* Session */ || storage === 4 /* Writable */;
68
+ }
69
+ function admittedPlacementStorages(constraint) {
70
+ if (constraint !== 4 /* Writable */) return [constraint];
71
+ return [2 /* Save */, 3 /* Session */];
72
+ }
65
73
  function constraintDefaultStorage(constraint) {
66
74
  if (constraint === null) return 3 /* Session */;
67
75
  if (constraint === 4 /* Writable */) {
@@ -84,6 +92,15 @@ function createWritableAncestorLookup(memberById2) {
84
92
  };
85
93
  return lookup;
86
94
  }
95
+ function isChildStorageAllowed(context, child2) {
96
+ if (child2 === 0 /* Inherit */) return true;
97
+ if (context === 5 /* User */) return false;
98
+ if (child2 === 5 /* User */) return false;
99
+ if (child2 === 2 /* Save */) {
100
+ return context !== 3 /* Session */;
101
+ }
102
+ return true;
103
+ }
87
104
  var MemberStorageKind;
88
105
  var init_member_storage = __esm({
89
106
  "../src/models/members/member-storage.ts"() {
@@ -94,6 +111,7 @@ var init_member_storage = __esm({
94
111
  MemberStorageKind3[MemberStorageKind3["Save"] = 2] = "Save";
95
112
  MemberStorageKind3[MemberStorageKind3["Session"] = 3] = "Session";
96
113
  MemberStorageKind3[MemberStorageKind3["Writable"] = 4] = "Writable";
114
+ MemberStorageKind3[MemberStorageKind3["User"] = 5] = "User";
97
115
  return MemberStorageKind3;
98
116
  })(MemberStorageKind || {});
99
117
  }
@@ -350,6 +368,255 @@ var init_member_subtree_distribution = __esm({
350
368
  }
351
369
  });
352
370
 
371
+ // node_modules/uuid/dist-node/regex.js
372
+ var regex_default;
373
+ var init_regex = __esm({
374
+ "node_modules/uuid/dist-node/regex.js"() {
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
+ }
377
+ });
378
+
379
+ // node_modules/uuid/dist-node/validate.js
380
+ function validate(uuid) {
381
+ return typeof uuid === "string" && regex_default.test(uuid);
382
+ }
383
+ var validate_default;
384
+ var init_validate = __esm({
385
+ "node_modules/uuid/dist-node/validate.js"() {
386
+ init_regex();
387
+ validate_default = validate;
388
+ }
389
+ });
390
+
391
+ // node_modules/uuid/dist-node/parse.js
392
+ function parse(uuid) {
393
+ if (!validate_default(uuid)) {
394
+ throw TypeError("Invalid UUID");
395
+ }
396
+ let v;
397
+ return Uint8Array.of((v = parseInt(uuid.slice(0, 8), 16)) >>> 24, v >>> 16 & 255, v >>> 8 & 255, v & 255, (v = parseInt(uuid.slice(9, 13), 16)) >>> 8, v & 255, (v = parseInt(uuid.slice(14, 18), 16)) >>> 8, v & 255, (v = parseInt(uuid.slice(19, 23), 16)) >>> 8, v & 255, (v = parseInt(uuid.slice(24, 36), 16)) / 1099511627776 & 255, v / 4294967296 & 255, v >>> 24 & 255, v >>> 16 & 255, v >>> 8 & 255, v & 255);
398
+ }
399
+ var parse_default;
400
+ var init_parse = __esm({
401
+ "node_modules/uuid/dist-node/parse.js"() {
402
+ init_validate();
403
+ parse_default = parse;
404
+ }
405
+ });
406
+
407
+ // node_modules/uuid/dist-node/stringify.js
408
+ function unsafeStringify(arr, offset = 0) {
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
+ }
411
+ var byteToHex;
412
+ var init_stringify = __esm({
413
+ "node_modules/uuid/dist-node/stringify.js"() {
414
+ byteToHex = [];
415
+ for (let i2 = 0; i2 < 256; ++i2) {
416
+ byteToHex.push((i2 + 256).toString(16).slice(1));
417
+ }
418
+ }
419
+ });
420
+
421
+ // node_modules/uuid/dist-node/rng.js
422
+ function rng() {
423
+ return crypto.getRandomValues(rnds8);
424
+ }
425
+ var rnds8;
426
+ var init_rng = __esm({
427
+ "node_modules/uuid/dist-node/rng.js"() {
428
+ rnds8 = new Uint8Array(16);
429
+ }
430
+ });
431
+
432
+ // node_modules/uuid/dist-node/v35.js
433
+ function stringToBytes(str) {
434
+ str = unescape(encodeURIComponent(str));
435
+ const bytes = new Uint8Array(str.length);
436
+ for (let i2 = 0; i2 < str.length; ++i2) {
437
+ bytes[i2] = str.charCodeAt(i2);
438
+ }
439
+ return bytes;
440
+ }
441
+ function v35(version, hash, value, namespace, buf, offset) {
442
+ const valueBytes = typeof value === "string" ? stringToBytes(value) : value;
443
+ const namespaceBytes = typeof namespace === "string" ? parse_default(namespace) : namespace;
444
+ if (typeof namespace === "string") {
445
+ namespace = parse_default(namespace);
446
+ }
447
+ if (namespace?.length !== 16) {
448
+ throw TypeError("Namespace must be array-like (16 iterable integer values, 0-255)");
449
+ }
450
+ let bytes = new Uint8Array(16 + valueBytes.length);
451
+ bytes.set(namespaceBytes);
452
+ bytes.set(valueBytes, namespaceBytes.length);
453
+ bytes = hash(bytes);
454
+ bytes[6] = bytes[6] & 15 | version;
455
+ bytes[8] = bytes[8] & 63 | 128;
456
+ if (buf) {
457
+ offset ??= 0;
458
+ if (offset < 0 || offset + 16 > buf.length) {
459
+ throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
460
+ }
461
+ for (let i2 = 0; i2 < 16; ++i2) {
462
+ buf[offset + i2] = bytes[i2];
463
+ }
464
+ return buf;
465
+ }
466
+ return unsafeStringify(bytes);
467
+ }
468
+ var DNS, URL2;
469
+ var init_v35 = __esm({
470
+ "node_modules/uuid/dist-node/v35.js"() {
471
+ init_parse();
472
+ init_stringify();
473
+ DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
474
+ URL2 = "6ba7b811-9dad-11d1-80b4-00c04fd430c8";
475
+ }
476
+ });
477
+
478
+ // node_modules/uuid/dist-node/v4.js
479
+ function v4(options, buf, offset) {
480
+ if (!buf && !options && crypto.randomUUID) {
481
+ return crypto.randomUUID();
482
+ }
483
+ return _v4(options, buf, offset);
484
+ }
485
+ function _v4(options, buf, offset) {
486
+ options = options || {};
487
+ const rnds = options.random ?? options.rng?.() ?? rng();
488
+ if (rnds.length < 16) {
489
+ throw new Error("Random bytes length must be >= 16");
490
+ }
491
+ rnds[6] = rnds[6] & 15 | 64;
492
+ rnds[8] = rnds[8] & 63 | 128;
493
+ if (buf) {
494
+ offset = offset || 0;
495
+ if (offset < 0 || offset + 16 > buf.length) {
496
+ throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
497
+ }
498
+ for (let i2 = 0; i2 < 16; ++i2) {
499
+ buf[offset + i2] = rnds[i2];
500
+ }
501
+ return buf;
502
+ }
503
+ return unsafeStringify(rnds);
504
+ }
505
+ var v4_default;
506
+ var init_v4 = __esm({
507
+ "node_modules/uuid/dist-node/v4.js"() {
508
+ init_rng();
509
+ init_stringify();
510
+ v4_default = v4;
511
+ }
512
+ });
513
+
514
+ // node_modules/uuid/dist-node/sha1.js
515
+ import { createHash } from "node:crypto";
516
+ function sha1(bytes) {
517
+ if (Array.isArray(bytes)) {
518
+ bytes = Buffer.from(bytes);
519
+ } else if (typeof bytes === "string") {
520
+ bytes = Buffer.from(bytes, "utf8");
521
+ }
522
+ return createHash("sha1").update(bytes).digest();
523
+ }
524
+ var sha1_default;
525
+ var init_sha1 = __esm({
526
+ "node_modules/uuid/dist-node/sha1.js"() {
527
+ sha1_default = sha1;
528
+ }
529
+ });
530
+
531
+ // node_modules/uuid/dist-node/v5.js
532
+ function v5(value, namespace, buf, offset) {
533
+ return v35(80, sha1_default, value, namespace, buf, offset);
534
+ }
535
+ var v5_default;
536
+ var init_v5 = __esm({
537
+ "node_modules/uuid/dist-node/v5.js"() {
538
+ init_sha1();
539
+ init_v35();
540
+ v5.DNS = DNS;
541
+ v5.URL = URL2;
542
+ v5_default = v5;
543
+ }
544
+ });
545
+
546
+ // node_modules/uuid/dist-node/index.js
547
+ var init_dist_node = __esm({
548
+ "node_modules/uuid/dist-node/index.js"() {
549
+ init_v4();
550
+ init_v5();
551
+ }
552
+ });
553
+
554
+ // ../src/models/project/project-root-members.ts
555
+ function projectRootMemberIds(project) {
556
+ return PROJECT_ROOTS.map((root) => project[root.projectField]);
557
+ }
558
+ function buildRootStorageMap(project) {
559
+ const map = /* @__PURE__ */ new Map();
560
+ for (const root of PROJECT_ROOTS) {
561
+ map.set(project[root.projectField], root.storage);
562
+ }
563
+ return map;
564
+ }
565
+ function writtenRootStorage(project, memberIds) {
566
+ const map = /* @__PURE__ */ new Map();
567
+ for (const root of PROJECT_ROOTS) {
568
+ const memberId = project[root.projectField];
569
+ if (memberIds.has(memberId)) map.set(memberId, root.storage);
570
+ }
571
+ return map;
572
+ }
573
+ function isProjectRootMember(project, memberId) {
574
+ return PROJECT_ROOTS.some((root) => project[root.projectField] === memberId);
575
+ }
576
+ function projectRootMembersInDisplayOrder(project) {
577
+ return PROJECT_ROOTS.map((root) => ({
578
+ root,
579
+ memberId: project[root.projectField]
580
+ }));
581
+ }
582
+ var PROJECT_ROOTS;
583
+ var init_project_root_members = __esm({
584
+ "../src/models/project/project-root-members.ts"() {
585
+ "use strict";
586
+ init_member_storage();
587
+ PROJECT_ROOTS = [
588
+ {
589
+ key: "Assets",
590
+ choice: "assets",
591
+ projectField: "rootAssetsMemberId",
592
+ label: "Assets",
593
+ storage: 1 /* Immutable */
594
+ },
595
+ {
596
+ key: "Save",
597
+ choice: "save",
598
+ projectField: "rootSaveFileMemberId",
599
+ label: "Save",
600
+ storage: 2 /* Save */
601
+ },
602
+ {
603
+ key: "Session",
604
+ choice: "session",
605
+ projectField: "rootSessionMemberId",
606
+ label: "Session",
607
+ storage: 3 /* Session */
608
+ },
609
+ {
610
+ key: "User",
611
+ choice: "user",
612
+ projectField: "rootUserMemberId",
613
+ label: "User",
614
+ storage: 5 /* User */
615
+ }
616
+ ];
617
+ }
618
+ });
619
+
353
620
  // ../src/models/core/structural-narrow.ts
354
621
  function isObject(value) {
355
622
  if (typeof value !== "object") return false;
@@ -4977,189 +5244,6 @@ var init_inheritance = __esm({
4977
5244
  }
4978
5245
  });
4979
5246
 
4980
- // node_modules/uuid/dist-node/regex.js
4981
- var regex_default;
4982
- var init_regex = __esm({
4983
- "node_modules/uuid/dist-node/regex.js"() {
4984
- 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;
4985
- }
4986
- });
4987
-
4988
- // node_modules/uuid/dist-node/validate.js
4989
- function validate(uuid) {
4990
- return typeof uuid === "string" && regex_default.test(uuid);
4991
- }
4992
- var validate_default;
4993
- var init_validate = __esm({
4994
- "node_modules/uuid/dist-node/validate.js"() {
4995
- init_regex();
4996
- validate_default = validate;
4997
- }
4998
- });
4999
-
5000
- // node_modules/uuid/dist-node/parse.js
5001
- function parse(uuid) {
5002
- if (!validate_default(uuid)) {
5003
- throw TypeError("Invalid UUID");
5004
- }
5005
- let v;
5006
- return Uint8Array.of((v = parseInt(uuid.slice(0, 8), 16)) >>> 24, v >>> 16 & 255, v >>> 8 & 255, v & 255, (v = parseInt(uuid.slice(9, 13), 16)) >>> 8, v & 255, (v = parseInt(uuid.slice(14, 18), 16)) >>> 8, v & 255, (v = parseInt(uuid.slice(19, 23), 16)) >>> 8, v & 255, (v = parseInt(uuid.slice(24, 36), 16)) / 1099511627776 & 255, v / 4294967296 & 255, v >>> 24 & 255, v >>> 16 & 255, v >>> 8 & 255, v & 255);
5007
- }
5008
- var parse_default;
5009
- var init_parse = __esm({
5010
- "node_modules/uuid/dist-node/parse.js"() {
5011
- init_validate();
5012
- parse_default = parse;
5013
- }
5014
- });
5015
-
5016
- // node_modules/uuid/dist-node/stringify.js
5017
- function unsafeStringify(arr, offset = 0) {
5018
- 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();
5019
- }
5020
- var byteToHex;
5021
- var init_stringify = __esm({
5022
- "node_modules/uuid/dist-node/stringify.js"() {
5023
- byteToHex = [];
5024
- for (let i2 = 0; i2 < 256; ++i2) {
5025
- byteToHex.push((i2 + 256).toString(16).slice(1));
5026
- }
5027
- }
5028
- });
5029
-
5030
- // node_modules/uuid/dist-node/rng.js
5031
- function rng() {
5032
- return crypto.getRandomValues(rnds8);
5033
- }
5034
- var rnds8;
5035
- var init_rng = __esm({
5036
- "node_modules/uuid/dist-node/rng.js"() {
5037
- rnds8 = new Uint8Array(16);
5038
- }
5039
- });
5040
-
5041
- // node_modules/uuid/dist-node/v35.js
5042
- function stringToBytes(str) {
5043
- str = unescape(encodeURIComponent(str));
5044
- const bytes = new Uint8Array(str.length);
5045
- for (let i2 = 0; i2 < str.length; ++i2) {
5046
- bytes[i2] = str.charCodeAt(i2);
5047
- }
5048
- return bytes;
5049
- }
5050
- function v35(version, hash, value, namespace, buf, offset) {
5051
- const valueBytes = typeof value === "string" ? stringToBytes(value) : value;
5052
- const namespaceBytes = typeof namespace === "string" ? parse_default(namespace) : namespace;
5053
- if (typeof namespace === "string") {
5054
- namespace = parse_default(namespace);
5055
- }
5056
- if (namespace?.length !== 16) {
5057
- throw TypeError("Namespace must be array-like (16 iterable integer values, 0-255)");
5058
- }
5059
- let bytes = new Uint8Array(16 + valueBytes.length);
5060
- bytes.set(namespaceBytes);
5061
- bytes.set(valueBytes, namespaceBytes.length);
5062
- bytes = hash(bytes);
5063
- bytes[6] = bytes[6] & 15 | version;
5064
- bytes[8] = bytes[8] & 63 | 128;
5065
- if (buf) {
5066
- offset ??= 0;
5067
- if (offset < 0 || offset + 16 > buf.length) {
5068
- throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
5069
- }
5070
- for (let i2 = 0; i2 < 16; ++i2) {
5071
- buf[offset + i2] = bytes[i2];
5072
- }
5073
- return buf;
5074
- }
5075
- return unsafeStringify(bytes);
5076
- }
5077
- var DNS, URL2;
5078
- var init_v35 = __esm({
5079
- "node_modules/uuid/dist-node/v35.js"() {
5080
- init_parse();
5081
- init_stringify();
5082
- DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
5083
- URL2 = "6ba7b811-9dad-11d1-80b4-00c04fd430c8";
5084
- }
5085
- });
5086
-
5087
- // node_modules/uuid/dist-node/v4.js
5088
- function v4(options, buf, offset) {
5089
- if (!buf && !options && crypto.randomUUID) {
5090
- return crypto.randomUUID();
5091
- }
5092
- return _v4(options, buf, offset);
5093
- }
5094
- function _v4(options, buf, offset) {
5095
- options = options || {};
5096
- const rnds = options.random ?? options.rng?.() ?? rng();
5097
- if (rnds.length < 16) {
5098
- throw new Error("Random bytes length must be >= 16");
5099
- }
5100
- rnds[6] = rnds[6] & 15 | 64;
5101
- rnds[8] = rnds[8] & 63 | 128;
5102
- if (buf) {
5103
- offset = offset || 0;
5104
- if (offset < 0 || offset + 16 > buf.length) {
5105
- throw new RangeError(`UUID byte range ${offset}:${offset + 15} is out of buffer bounds`);
5106
- }
5107
- for (let i2 = 0; i2 < 16; ++i2) {
5108
- buf[offset + i2] = rnds[i2];
5109
- }
5110
- return buf;
5111
- }
5112
- return unsafeStringify(rnds);
5113
- }
5114
- var v4_default;
5115
- var init_v4 = __esm({
5116
- "node_modules/uuid/dist-node/v4.js"() {
5117
- init_rng();
5118
- init_stringify();
5119
- v4_default = v4;
5120
- }
5121
- });
5122
-
5123
- // node_modules/uuid/dist-node/sha1.js
5124
- import { createHash } from "node:crypto";
5125
- function sha1(bytes) {
5126
- if (Array.isArray(bytes)) {
5127
- bytes = Buffer.from(bytes);
5128
- } else if (typeof bytes === "string") {
5129
- bytes = Buffer.from(bytes, "utf8");
5130
- }
5131
- return createHash("sha1").update(bytes).digest();
5132
- }
5133
- var sha1_default;
5134
- var init_sha1 = __esm({
5135
- "node_modules/uuid/dist-node/sha1.js"() {
5136
- sha1_default = sha1;
5137
- }
5138
- });
5139
-
5140
- // node_modules/uuid/dist-node/v5.js
5141
- function v5(value, namespace, buf, offset) {
5142
- return v35(80, sha1_default, value, namespace, buf, offset);
5143
- }
5144
- var v5_default;
5145
- var init_v5 = __esm({
5146
- "node_modules/uuid/dist-node/v5.js"() {
5147
- init_sha1();
5148
- init_v35();
5149
- v5.DNS = DNS;
5150
- v5.URL = URL2;
5151
- v5_default = v5;
5152
- }
5153
- });
5154
-
5155
- // node_modules/uuid/dist-node/index.js
5156
- var init_dist_node = __esm({
5157
- "node_modules/uuid/dist-node/index.js"() {
5158
- init_v4();
5159
- init_v5();
5160
- }
5161
- });
5162
-
5163
5247
  // ../src/models/system-record-id.ts
5164
5248
  function isSystemRecordId(id2) {
5165
5249
  return id2.startsWith(SYSTEM_RECORD_ID_PREFIX);
@@ -8667,11 +8751,7 @@ function assertReadOnlyMembersValid(document) {
8667
8751
  }
8668
8752
  }
8669
8753
  }
8670
- const rootMemberIds2 = /* @__PURE__ */ new Set([
8671
- document.project.rootAssetsMemberId,
8672
- document.project.rootSaveFileMemberId,
8673
- document.project.rootSessionMemberId
8674
- ]);
8754
+ const rootMemberIds = new Set(projectRootMemberIds(document.project));
8675
8755
  for (const member of document.members) {
8676
8756
  const resolved = resolveMember(member, document.members);
8677
8757
  if (!isMemberLookupBase(resolved)) continue;
@@ -8690,7 +8770,7 @@ function assertReadOnlyMembersValid(document) {
8690
8770
  );
8691
8771
  }
8692
8772
  const ownerContext = readOnlyOwnerContext(placements);
8693
- if (rootMemberIds2.has(member.id) || entryMemberIds.has(member.id) || bindingMemberIds.has(member.id)) {
8773
+ if (rootMemberIds.has(member.id) || entryMemberIds.has(member.id) || bindingMemberIds.has(member.id)) {
8694
8774
  throw new Error(
8695
8775
  `Read-only member "${member.name}" (${member.id}) cannot be a root, collection entry template, or generic binding artifact; ${ownerContext}.`
8696
8776
  );
@@ -8961,6 +9041,7 @@ var READ_ONLY_SYNTHETIC_VALUE_ID_PREFIX;
8961
9041
  var init_read_only_members = __esm({
8962
9042
  "../src/models/members/read-only-members.ts"() {
8963
9043
  "use strict";
9044
+ init_project_root_members();
8964
9045
  init_inheritance();
8965
9046
  init_classes();
8966
9047
  init_project_schema_index();
@@ -13052,6 +13133,172 @@ function declaredMemberStorageFromIndex(memberId, memberIndex) {
13052
13133
  );
13053
13134
  return declared ?? 0 /* Inherit */;
13054
13135
  }
13136
+ function memberStorageLabel(value) {
13137
+ if (!isMemberStorageKind(value)) return String(value);
13138
+ switch (value) {
13139
+ case 0 /* Inherit */:
13140
+ return "inherit";
13141
+ case 1 /* Immutable */:
13142
+ return "immutable";
13143
+ case 2 /* Save */:
13144
+ return "save";
13145
+ case 3 /* Session */:
13146
+ return "session";
13147
+ case 4 /* Writable */:
13148
+ return "writable";
13149
+ case 5 /* User */:
13150
+ return "user";
13151
+ }
13152
+ }
13153
+ function constraintLabel(constraint) {
13154
+ return admittedPlacementStorages(constraint).map((storage) => `"${memberStorageLabel(storage)}"`).join(" or ");
13155
+ }
13156
+ function ownStorage(member) {
13157
+ return member.storage;
13158
+ }
13159
+ function memberLabel(member, memberId) {
13160
+ if (!member) return `"${memberId}"`;
13161
+ return `"${member.name}" (${memberId})`;
13162
+ }
13163
+ function validateDocumentStorage(ctx) {
13164
+ const resolver = new EffectiveStorageResolver(ctx);
13165
+ const errors = [];
13166
+ const userClasses = resolver.userClassIds();
13167
+ const classById = new Map(
13168
+ ctx.classes.map((schemaClass2) => [schemaClass2.id, schemaClass2])
13169
+ );
13170
+ for (const [rootId, stamped] of ctx.rootStorage) {
13171
+ const root = resolver.member(rootId);
13172
+ if (root === void 0) continue;
13173
+ const declared = root.storage;
13174
+ if (declared !== stamped) {
13175
+ errors.push({
13176
+ message: `Project root member ${memberLabel(root, rootId)} must declare storage "${memberStorageLabel(stamped)}" but declares "${memberStorageLabel(declared)}".`
13177
+ });
13178
+ }
13179
+ }
13180
+ const userClassMemberIds = /* @__PURE__ */ new Set();
13181
+ for (const classId of userClasses) {
13182
+ const schemaClass2 = classById.get(classId);
13183
+ if (schemaClass2 === void 0) continue;
13184
+ for (const memberId of Object.values(schemaClass2.schema)) {
13185
+ userClassMemberIds.add(memberId);
13186
+ const member = resolver.member(memberId);
13187
+ if (member === void 0) continue;
13188
+ const own = ownStorage(member);
13189
+ if (own !== void 0 && own !== 0 /* Inherit */) {
13190
+ errors.push({
13191
+ message: `${resolver.placementLabel(member)}: Members of User classes can't declare storage. They are always User.`
13192
+ });
13193
+ }
13194
+ if (isStaticMember(member) && memberKindSupportsStorage(member.kind)) {
13195
+ errors.push({
13196
+ message: `${resolver.placementLabel(member)}: User classes can't declare static stored members.`
13197
+ });
13198
+ }
13199
+ }
13200
+ }
13201
+ for (const member of ctx.members) {
13202
+ const declared = resolver.declaredStorage(member.id);
13203
+ if (declared === 4 /* Writable */ && isStaticMember(member)) {
13204
+ errors.push({
13205
+ message: `Static member ${memberLabel(member, member.id)} cannot declare storage "writable"; a static member has no placement parent to resolve it against.`
13206
+ });
13207
+ }
13208
+ const own = ownStorage(member);
13209
+ if (own === 1 /* Immutable */ || own === 0 /* Inherit */) {
13210
+ const writableAncestorId = resolver.writableAncestorId(member.id);
13211
+ if (writableAncestorId !== null) {
13212
+ errors.push({
13213
+ message: `Member ${memberLabel(member, member.id)} declares storage "${memberStorageLabel(own)}" but overrides Writable member ${memberLabel(resolver.member(writableAncestorId), writableAncestorId)}; an override of a Writable member may declare "writable", "save", or "session", or omit storage.`
13214
+ });
13215
+ }
13216
+ }
13217
+ if (declared === 0 /* Inherit */) continue;
13218
+ if (ctx.rootStorage.has(member.id)) continue;
13219
+ if (declared === 5 /* User */) {
13220
+ errors.push({
13221
+ message: `${resolver.placementLabel(member)}: Only classes can be constrained to User. Members under \`root.User\` are User already.`
13222
+ });
13223
+ continue;
13224
+ }
13225
+ if (userClassMemberIds.has(member.id)) continue;
13226
+ for (const parentId of resolver.parentMemberIds(member.id)) {
13227
+ for (const context of resolver.effectiveStorages(parentId)) {
13228
+ if (isChildStorageAllowed(context, declared)) continue;
13229
+ errors.push({
13230
+ message: context === 5 /* User */ ? `${resolver.placementLabel(member)}: Members of User classes can't declare storage. They are always User.` : `Member ${memberLabel(member, member.id)} declares storage "${memberStorageLabel(declared)}" but is placed under ${memberLabel(resolver.member(parentId), parentId)} whose effective storage is "${memberStorageLabel(context)}"; "save" persistence cannot outlive a "session" container.`
13231
+ });
13232
+ }
13233
+ }
13234
+ }
13235
+ for (const schemaClass2 of ctx.classes) {
13236
+ if (userClasses.has(schemaClass2.id) && schemaClass2.extendsClassId !== void 0 && !userClasses.has(schemaClass2.extendsClassId)) {
13237
+ const base = classById.get(schemaClass2.extendsClassId);
13238
+ errors.push({
13239
+ message: `User class \`${schemaClass2.name}\` can't extend \`${base?.name ?? schemaClass2.extendsClassId}\`, which isn't a User class.`
13240
+ });
13241
+ }
13242
+ if (schemaClass2.allowedStorage == null) continue;
13243
+ if (schemaClass2.extendsClassId === void 0) continue;
13244
+ const inherited = resolver.allowedStorage(schemaClass2.extendsClassId);
13245
+ if (inherited === null) continue;
13246
+ if (allowedStorageAdmits(inherited, schemaClass2.allowedStorage)) continue;
13247
+ errors.push({
13248
+ message: `Class "${schemaClass2.name}" (${schemaClass2.id}) declares allowedStorage "${memberStorageLabel(schemaClass2.allowedStorage)}" but inherits allowedStorage "${memberStorageLabel(inherited)}" from its extends chain; constraints are narrow-only and may not widen or contradict an inherited value.`
13249
+ });
13250
+ }
13251
+ for (const member of ctx.members) {
13252
+ if (member.kind !== 7 /* Class */) continue;
13253
+ const classId = resolver.resolvedClassId(member.id);
13254
+ if (classId === void 0) continue;
13255
+ const constraint = resolver.allowedStorage(classId);
13256
+ const className = classById.get(classId)?.name ?? classId;
13257
+ for (const effective of resolver.effectiveStorages(member.id)) {
13258
+ if (effective === 5 /* User */ && constraint !== 5 /* User */) {
13259
+ errors.push({
13260
+ message: `\`${resolver.placementLabel(member)}\` is User data, so \`${className}\` needs \`@storage(allowed: .User)\`.`
13261
+ });
13262
+ continue;
13263
+ }
13264
+ if (constraint === null) continue;
13265
+ if (allowedStorageAdmits(constraint, effective)) continue;
13266
+ errors.push({
13267
+ message: `Member ${memberLabel(member, member.id)} places class "${className}" at an effective storage of "${memberStorageLabel(effective)}", but the class's allowedStorage restricts placements to ${constraintLabel(constraint)}.`
13268
+ });
13269
+ }
13270
+ }
13271
+ for (const member of ctx.members) {
13272
+ if (member.kind !== 9 /* Lookup */ && !isMemberLookupBase(member)) {
13273
+ continue;
13274
+ }
13275
+ const collectionId = resolver.resolvedCollectionMemberId(member.id);
13276
+ if (collectionId === void 0) continue;
13277
+ const effective = resolver.effectiveStorages(member.id);
13278
+ const collectionEffective = resolver.effectiveStorages(collectionId);
13279
+ const targetsUser = collectionEffective.has(5 /* User */);
13280
+ if (effective.has(5 /* User */)) {
13281
+ for (const storage of collectionEffective) {
13282
+ if (storage === 5 /* User */) continue;
13283
+ errors.push({
13284
+ message: `${resolver.placementLabel(member)}: User lookups can only target collections under \`root.User\`.`
13285
+ });
13286
+ break;
13287
+ }
13288
+ }
13289
+ if (targetsUser && (effective.has(2 /* Save */) || effective.has(1 /* Immutable */))) {
13290
+ errors.push({
13291
+ message: `${resolver.placementLabel(member)}: Save data can't look up \`root.User\`, because the user file isn't part of the save.`
13292
+ });
13293
+ }
13294
+ if (!effective.has(2 /* Save */)) continue;
13295
+ if (!collectionEffective.has(3 /* Session */)) continue;
13296
+ errors.push({
13297
+ message: `Lookup member ${memberLabel(member, member.id)} is Save-effective but targets collection ${memberLabel(resolver.member(collectionId), collectionId)} which is Session-effective; a persisted selection cannot reference session-scoped values.`
13298
+ });
13299
+ }
13300
+ return errors;
13301
+ }
13055
13302
  var EffectiveStorageResolver;
13056
13303
  var init_effective_storage = __esm({
13057
13304
  "../src/models/members/effective-storage.ts"() {
@@ -13059,7 +13306,6 @@ var init_effective_storage = __esm({
13059
13306
  init_member_storage();
13060
13307
  init_member_kinds();
13061
13308
  init_structural_narrow();
13062
- init_classes();
13063
13309
  init_inheritance();
13064
13310
  init_project_schema_index();
13065
13311
  EffectiveStorageResolver = class {
@@ -13076,6 +13322,38 @@ var init_effective_storage = __esm({
13076
13322
  (id2) => this.schemaIndex.member(id2)
13077
13323
  );
13078
13324
  resolving = /* @__PURE__ */ new Set();
13325
+ allowedByClassId = /* @__PURE__ */ new Map();
13326
+ userClasses;
13327
+ /**
13328
+ * `resolveAllowedStorage` over this context, memoized along each extends
13329
+ * chain so a whole-document pass stays O(classes).
13330
+ */
13331
+ allowedStorage(classId) {
13332
+ const cached = this.allowedByClassId.get(classId);
13333
+ if (cached !== void 0) return cached;
13334
+ this.allowedByClassId.set(classId, null);
13335
+ const schemaClass2 = this.schemaIndex.classesById.get(classId);
13336
+ let resolved = null;
13337
+ if (schemaClass2?.allowedStorage != null) {
13338
+ resolved = schemaClass2.allowedStorage;
13339
+ } else if (schemaClass2?.extendsClassId !== void 0) {
13340
+ resolved = this.allowedStorage(schemaClass2.extendsClassId);
13341
+ }
13342
+ this.allowedByClassId.set(classId, resolved);
13343
+ return resolved;
13344
+ }
13345
+ /** P104 §1.3: classes whose resolved `allowedStorage` is `User`. */
13346
+ userClassIds() {
13347
+ if (this.userClasses === void 0) {
13348
+ this.userClasses = /* @__PURE__ */ new Set();
13349
+ for (const schemaClass2 of this.ctx.classes) {
13350
+ if (this.allowedStorage(schemaClass2.id) === 5 /* User */) {
13351
+ this.userClasses.add(schemaClass2.id);
13352
+ }
13353
+ }
13354
+ }
13355
+ return this.userClasses;
13356
+ }
13079
13357
  /** Declared storage of `memberId`, resolved through the extends chain. */
13080
13358
  declaredStorage(memberId) {
13081
13359
  return declaredMemberStorageFromIndex(
@@ -13119,7 +13397,7 @@ var init_effective_storage = __esm({
13119
13397
  result.add(declared);
13120
13398
  } else if (isStaticMember(this.schemaIndex.member(memberId))) {
13121
13399
  const declaringClassId = this.schemaIndex.firstSchemaPlacement(memberId)?.ownerClass.id;
13122
- const constraint = declaringClassId === void 0 ? null : resolveAllowedStorage(declaringClassId, this.ctx.classes);
13400
+ const constraint = declaringClassId === void 0 ? null : this.allowedStorage(declaringClassId);
13123
13401
  result.add(
13124
13402
  childEffectiveStorage(
13125
13403
  declared,
@@ -13222,6 +13500,11 @@ var init_effective_storage = __esm({
13222
13500
  member(memberId) {
13223
13501
  return this.schemaIndex.member(memberId);
13224
13502
  }
13503
+ /** `Owner.Member` for a schema member, else the member's own name. */
13504
+ placementLabel(member) {
13505
+ const owner = this.schemaIndex.firstSchemaPlacement(member.id)?.ownerClass;
13506
+ return owner === void 0 ? member.name : `${owner.name}.${member.name}`;
13507
+ }
13225
13508
  buildParentEdges() {
13226
13509
  const addEdge = (childId, parentId) => {
13227
13510
  const existing = this.parentIdsByMemberId.get(childId);
@@ -23445,6 +23728,189 @@ var init_project = __esm({
23445
23728
  }
23446
23729
  });
23447
23730
 
23731
+ // ../packages/neoscript-language/src/project-source-lifecycle-hooks.ts
23732
+ function isUnityLifecycleInterfaceName(name) {
23733
+ return name.startsWith(UNITY_LIFECYCLE_INTERFACE_PREFIX);
23734
+ }
23735
+ function isScriptRuntimeInterface(declaration) {
23736
+ const kind = declaration.annotations.find((annotation2) => annotation2.name === "system")?.arguments.find((argument2) => argument2.name === "kind")?.text.trim();
23737
+ return kind === ".ScriptRuntime";
23738
+ }
23739
+ function lifecycleHooksByMemberName(interfaces) {
23740
+ const hooks = /* @__PURE__ */ new Map();
23741
+ for (const declaration of interfaces) {
23742
+ if (!isScriptRuntimeInterface(declaration)) continue;
23743
+ for (const member of declaration.members) {
23744
+ if (member.kind !== "function") continue;
23745
+ hooks.set(member.name.toLowerCase(), {
23746
+ interfaceName: declaration.name,
23747
+ member,
23748
+ ...declaration.docsText === void 0 ? {} : { docsText: declaration.docsText },
23749
+ unity: isUnityLifecycleInterfaceName(declaration.name)
23750
+ });
23751
+ }
23752
+ }
23753
+ return hooks;
23754
+ }
23755
+ function reservedLifecycleMemberMessage(memberName, interfaceName, owner) {
23756
+ const fix = owner === "class" ? "Implement the interface with a public method" : "Extend the interface";
23757
+ return `'${memberName}' is reserved for '${interfaceName}'. ${fix}, or rename the member.`;
23758
+ }
23759
+ var NEO_LIFECYCLE_OBJECT_CLASS_NAME, UNITY_LIFECYCLE_INTERFACE_PREFIX;
23760
+ var init_project_source_lifecycle_hooks = __esm({
23761
+ "../packages/neoscript-language/src/project-source-lifecycle-hooks.ts"() {
23762
+ "use strict";
23763
+ NEO_LIFECYCLE_OBJECT_CLASS_NAME = "NeoObjectBase";
23764
+ UNITY_LIFECYCLE_INTERFACE_PREFIX = "INeoUnity";
23765
+ }
23766
+ });
23767
+
23768
+ // ../packages/neoscript-language/src/user-safe-types.ts
23769
+ function userUnsafeTypeName(type, project) {
23770
+ return userSafeTypeCheck(project).unsafeName(type);
23771
+ }
23772
+ function userSafeTypeCheck(project) {
23773
+ let check2 = checks.get(project);
23774
+ if (check2 === void 0) {
23775
+ check2 = new UserSafeTypeCheck(project);
23776
+ checks.set(project, check2);
23777
+ }
23778
+ return check2;
23779
+ }
23780
+ function userClassSignatureViolation(userClass, project) {
23781
+ const check2 = userSafeTypeCheck(project);
23782
+ for (const member of userClass.members) {
23783
+ if (member.inheritedFrom !== void 0) continue;
23784
+ const typeName = check2.symbolUnsafeName(member);
23785
+ if (typeName !== null) return { memberName: member.name, typeName };
23786
+ }
23787
+ for (const constructor2 of userClass.declaredConstructors ?? []) {
23788
+ for (const parameter4 of constructor2.parameters) {
23789
+ const typeName = check2.unsafeName(parameter4.type);
23790
+ if (typeName !== null) return { memberName: null, typeName };
23791
+ }
23792
+ }
23793
+ return null;
23794
+ }
23795
+ function userUnsafeTypeMessage(typeName, site) {
23796
+ return `User code can't use \`${typeName}\` in ${site}, because it isn't a User class.`;
23797
+ }
23798
+ var USER_DATA_WRITE_ERROR, checks, UserSafeTypeCheck;
23799
+ var init_user_safe_types = __esm({
23800
+ "../packages/neoscript-language/src/user-safe-types.ts"() {
23801
+ "use strict";
23802
+ init_project_source_lifecycle_hooks();
23803
+ USER_DATA_WRITE_ERROR = "Only User code can change User data. C# changes it through the generated user client.";
23804
+ checks = /* @__PURE__ */ new WeakMap();
23805
+ UserSafeTypeCheck = class {
23806
+ constructor(project) {
23807
+ this.project = project;
23808
+ }
23809
+ project;
23810
+ /** Final interface verdicts. */
23811
+ interfaces = /* @__PURE__ */ new Map();
23812
+ /**
23813
+ * Interfaces whose verdict waits on their cycle (Tarjan's SCC stack),
23814
+ * by stack position. One on the stack reads as safe until its cycle ends.
23815
+ */
23816
+ pending = /* @__PURE__ */ new Map();
23817
+ stack = [];
23818
+ /** The lowest stack position the current interface's check consulted. */
23819
+ lowestConsulted = Infinity;
23820
+ symbolUnsafeName(symbol) {
23821
+ for (const parameter4 of symbol.parameters ?? []) {
23822
+ const name = this.unsafeName(parameter4.type);
23823
+ if (name !== null) return name;
23824
+ }
23825
+ if (symbol.returnType !== void 0) {
23826
+ const name = this.unsafeName(symbol.returnType);
23827
+ if (name !== null) return name;
23828
+ }
23829
+ return this.unsafeName(symbol.type);
23830
+ }
23831
+ unsafeName(type) {
23832
+ switch (type.kind) {
23833
+ case "primitive":
23834
+ return null;
23835
+ case "typeParameter":
23836
+ return type.constraint === void 0 ? null : this.unsafeName(type.constraint);
23837
+ case "list":
23838
+ case "set":
23839
+ return this.unsafeName(type.elementType);
23840
+ case "dictionary":
23841
+ return this.unsafeName(type.keyType) ?? this.unsafeName(type.valueType);
23842
+ case "delegate":
23843
+ return this.firstUnsafe(type.parameterTypes) ?? this.unsafeName(type.returnType);
23844
+ case "action":
23845
+ return this.firstUnsafe(type.parameterTypes);
23846
+ case "named":
23847
+ return this.namedUnsafeName(type.typeId) ?? this.firstUnsafe(type.typeArguments ?? []);
23848
+ }
23849
+ }
23850
+ firstUnsafe(types) {
23851
+ for (const type of types) {
23852
+ const name = this.unsafeName(type);
23853
+ if (name !== null) return name;
23854
+ }
23855
+ return null;
23856
+ }
23857
+ namedUnsafeName(typeId) {
23858
+ const named = this.project.typeById.get(typeId);
23859
+ if (named === void 0) return null;
23860
+ switch (named.kind) {
23861
+ case "enum":
23862
+ case "builtin":
23863
+ return null;
23864
+ case "class":
23865
+ return named.userClass === true ? null : named.name;
23866
+ case "synthetic":
23867
+ return named.name;
23868
+ case "interface":
23869
+ return this.interfaceUnsafeName(named);
23870
+ }
23871
+ }
23872
+ // An interface is unsafe when it reaches an unsafe type, so a cycle alone
23873
+ // proves nothing. Interfaces in one cycle reach each other and share a
23874
+ // verdict, settled once the cycle's first interface finishes; each
23875
+ // interface is scanned once.
23876
+ interfaceUnsafeName(named) {
23877
+ if (isUnityLifecycleInterfaceName(named.name)) return named.name;
23878
+ const cached = this.interfaces.get(named.id);
23879
+ if (cached !== void 0) return cached;
23880
+ const pendingAt = this.pending.get(named.id);
23881
+ if (pendingAt !== void 0) {
23882
+ this.lowestConsulted = Math.min(this.lowestConsulted, pendingAt);
23883
+ return null;
23884
+ }
23885
+ const position = this.stack.length;
23886
+ this.stack.push(named);
23887
+ this.pending.set(named.id, position);
23888
+ const outerLowest = this.lowestConsulted;
23889
+ this.lowestConsulted = Infinity;
23890
+ let unsafe = false;
23891
+ for (const member of named.members) {
23892
+ if (this.symbolUnsafeName(member) === null) continue;
23893
+ unsafe = true;
23894
+ break;
23895
+ }
23896
+ if (unsafe || this.lowestConsulted >= position) {
23897
+ this.settleFrom(position, unsafe);
23898
+ this.lowestConsulted = outerLowest;
23899
+ } else {
23900
+ this.lowestConsulted = Math.min(outerLowest, this.lowestConsulted);
23901
+ }
23902
+ return unsafe ? named.name : null;
23903
+ }
23904
+ settleFrom(position, unsafe) {
23905
+ for (const settled of this.stack.splice(position)) {
23906
+ this.pending.delete(settled.id);
23907
+ this.interfaces.set(settled.id, unsafe ? settled.name : null);
23908
+ }
23909
+ }
23910
+ };
23911
+ }
23912
+ });
23913
+
23448
23914
  // ../packages/neoscript-language/src/source-map.ts
23449
23915
  function createNeoScriptSourceMap(code, fileText, range2, source, thisAlias) {
23450
23916
  const text = typeof fileText === "string" ? new SourceText(fileText) : fileText;
@@ -25786,8 +26252,10 @@ function namedPrimitive(name) {
25786
26252
  return null;
25787
26253
  }
25788
26254
  }
25789
- function toWritability(value) {
26255
+ function toWritability(value, writesUserData) {
25790
26256
  switch (value) {
26257
+ case "user":
26258
+ return writesUserData ? "runtime" /* Runtime */ : "readOnly" /* ReadOnly */;
25791
26259
  case "save":
25792
26260
  return "save" /* Save */;
25793
26261
  case "session":
@@ -26069,6 +26537,7 @@ var init_strict_resolver = __esm({
26069
26537
  init_declared_constructors();
26070
26538
  init_language_spec();
26071
26539
  init_project();
26540
+ init_user_safe_types();
26072
26541
  init_strict_ast();
26073
26542
  init_strict_compile_error();
26074
26543
  init_strict_ir();
@@ -26244,6 +26713,9 @@ var init_strict_resolver = __esm({
26244
26713
  };
26245
26714
  this.debugPosition = sourcePositionMapper(options.source);
26246
26715
  this.project = projectIndex ?? createProjectIndex(context.project);
26716
+ const lexicalClass = context.declaringType === void 0 ? context.thisClass : context.declaringType;
26717
+ this.userClassCode = lexicalClass?.kind === "named" && this.project.typeById.get(lexicalClass.typeId)?.userClass === true;
26718
+ this.writesUserData = this.userClassCode || context.projectSourceKind === "spec";
26247
26719
  this.listenerParameters = createListenerAnnotationProvider(
26248
26720
  this.project,
26249
26721
  context
@@ -26260,6 +26732,7 @@ var init_strict_resolver = __esm({
26260
26732
  const rootSymbol = context.project.roots.find(
26261
26733
  (candidate) => candidate.name === "root"
26262
26734
  );
26735
+ this.rootTypeId = rootSymbol?.type.kind === "named" ? rootSymbol.type.typeId : null;
26263
26736
  this.rootVariable = variable(
26264
26737
  "__root__",
26265
26738
  rootSymbol?.type ?? unknown,
@@ -26315,6 +26788,11 @@ var init_strict_resolver = __esm({
26315
26788
  functionControlBoundaries = [];
26316
26789
  debugSource;
26317
26790
  inferredExpressionReturnType;
26791
+ /** P104 §3.1. User class bodies reach only `root.User`. */
26792
+ userClassCode;
26793
+ rootTypeId;
26794
+ /** P104 §3.1. User class and test bodies may write User data. */
26795
+ writesUserData;
26318
26796
  initialScope() {
26319
26797
  const scope = new Scope(null, this.options.onFlowScope !== void 0);
26320
26798
  scope.define(
@@ -27844,6 +28322,9 @@ var init_strict_resolver = __esm({
27844
28322
  statement.pos
27845
28323
  );
27846
28324
  }
28325
+ if (writability === "readOnly" /* ReadOnly */ && target.userData) {
28326
+ throw new CompileError(USER_DATA_WRITE_ERROR, statement.pos);
28327
+ }
27847
28328
  if (writability === "readOnly" /* ReadOnly */) {
27848
28329
  throw new CompileError(
27849
28330
  `Cannot assign to a read-only ${this.describe(target.type)} value.`,
@@ -28037,7 +28518,7 @@ var init_strict_resolver = __esm({
28037
28518
  statement.pos
28038
28519
  );
28039
28520
  }
28040
- const writability = observed.writability;
28521
+ const writability = observed.userData ? "runtime" /* Runtime */ : observed.writability;
28041
28522
  if (writability !== "save" /* Save */ && writability !== "session" /* Session */ && writability !== "runtime" /* Runtime */ && writability !== "local" /* Local */) {
28042
28523
  throw new CompileError(
28043
28524
  "OnChanged requires a member whose storage is writable at gameplay runtime.",
@@ -28647,6 +29128,16 @@ var init_strict_resolver = __esm({
28647
29128
  expression.pos
28648
29129
  );
28649
29130
  const declaredTarget = this.project.typeByName.get(className);
29131
+ if (declaredTarget !== void 0) {
29132
+ this.assertUserSafe(
29133
+ { kind: "named", typeId: declaredTarget.id },
29134
+ "a `new` expression",
29135
+ expression.pos
29136
+ );
29137
+ }
29138
+ if (declaredTarget?.userClass === true && !this.writesUserData && this.context.storedConstructionReplay !== true) {
29139
+ throw new CompileError(USER_DATA_WRITE_ERROR, expression.pos);
29140
+ }
28650
29141
  if (declaredTarget?.kind === "class" && (expression.initializer?.length ?? 0) > 0 && this.context.storedConstructionReplay !== true) {
28651
29142
  return this.resolveExecutableObjectInitializer(
28652
29143
  declaredTarget,
@@ -29835,7 +30326,7 @@ var init_strict_resolver = __esm({
29835
30326
  type: member.type,
29836
30327
  symbol: member,
29837
30328
  ...member.writable ? {
29838
- writability: member.writability ? toWritability(member.writability) : "setter" /* Setter */
30329
+ writability: member.writability ? toWritability(member.writability, this.writesUserData) : "setter" /* Setter */
29839
30330
  } : {}
29840
30331
  };
29841
30332
  }
@@ -29846,7 +30337,9 @@ var init_strict_resolver = __esm({
29846
30337
  },
29847
30338
  type: member.type,
29848
30339
  symbol: member,
29849
- ...member.writability ? { writability: toWritability(member.writability) } : {}
30340
+ ...member.writability ? {
30341
+ writability: toWritability(member.writability, this.writesUserData)
30342
+ } : {}
29850
30343
  };
29851
30344
  }
29852
30345
  resolveMemberNatural(receiverAst, name, scope, pos, optional2, receiverValue) {
@@ -29916,6 +30409,11 @@ var init_strict_resolver = __esm({
29916
30409
  }
29917
30410
  const staticType = this.project.typeByName.get(receiverAst.name);
29918
30411
  if (staticType && staticType.kind !== "enum") {
30412
+ this.assertUserSafe(
30413
+ { kind: "named", typeId: staticType.id },
30414
+ "a static receiver",
30415
+ pos
30416
+ );
29919
30417
  if (optional2) {
29920
30418
  throw new CompileError(
29921
30419
  `Optional chaining is not valid on type receiver '${staticType.name}'.`,
@@ -30094,6 +30592,9 @@ var init_strict_resolver = __esm({
30094
30592
  );
30095
30593
  }
30096
30594
  this.assertMemberAccessible(type, member, pos);
30595
+ if (this.userClassCode && type.id === this.rootTypeId && member.writability !== "user") {
30596
+ throw new CompileError("User code can only read `root.User`.", pos);
30597
+ }
30097
30598
  if (member.static === true) {
30098
30599
  throw new CompileError(
30099
30600
  `Static member '${type.name}.${name}' must be accessed through the type name, not an instance.`,
@@ -30123,12 +30624,14 @@ var init_strict_resolver = __esm({
30123
30624
  ...nullPropagating ? { optionalChainType: memberType2 } : {},
30124
30625
  symbol: member,
30125
30626
  ...member.writable ? {
30126
- writability: member.writability ? toWritability(member.writability) : "setter" /* Setter */
30627
+ writability: member.writability ? toWritability(member.writability, this.writesUserData) : "setter" /* Setter */
30127
30628
  } : {}
30128
30629
  };
30129
30630
  }
30130
30631
  const writability = this.memberWritability(member, receiver);
30131
30632
  const entryWritability = member.lookup ? this.lookupEntryWritability(member, receiver) : void 0;
30633
+ const userData = this.memberUserData(member, receiver);
30634
+ const entryUserData = member.lookup ? this.lookupEntryUserData(member) : false;
30132
30635
  const lookupWire = member.lookup?.multiselect === true && memberType2.kind === "set" ? {
30133
30636
  type: 9 /* Lookup */,
30134
30637
  required: !isNullable(memberType2),
@@ -30150,7 +30653,9 @@ var init_strict_resolver = __esm({
30150
30653
  symbol: member,
30151
30654
  ...writability ? { writability } : {},
30152
30655
  ...lookupWire ? { wireType: lookupWire } : {},
30153
- ...entryWritability ? { entryWritability } : {}
30656
+ ...entryWritability ? { entryWritability } : {},
30657
+ userData,
30658
+ entryUserData
30154
30659
  };
30155
30660
  }
30156
30661
  throw new CompileError(
@@ -30236,20 +30741,33 @@ var init_strict_resolver = __esm({
30236
30741
  }
30237
30742
  memberWritability(member, receiver) {
30238
30743
  if (member.writability && !member.lookup) {
30239
- return toWritability(member.writability);
30744
+ return toWritability(member.writability, this.writesUserData);
30240
30745
  }
30241
30746
  if (!member.writable) return void 0;
30242
30747
  if (!member.lookup) {
30243
- return member.writability ? toWritability(member.writability) : receiver.entryWritability ?? receiver.writability ?? "save" /* Save */;
30748
+ return member.writability ? toWritability(member.writability, this.writesUserData) : receiver.entryWritability ?? receiver.writability ?? "save" /* Save */;
30749
+ }
30750
+ return member.writability ? toWritability(member.writability, this.writesUserData) : receiver.writability ?? "save" /* Save */;
30751
+ }
30752
+ /** Mirrors {@link memberWritability}'s inheritance for P104 User data. */
30753
+ memberUserData(member, receiver) {
30754
+ if (member.writability) return member.writability === "user";
30755
+ if (member.lookup || receiver.entryWritability === void 0) {
30756
+ return receiver.userData === true;
30244
30757
  }
30245
- return member.writability ? toWritability(member.writability) : receiver.writability ?? "save" /* Save */;
30758
+ return receiver.entryUserData === true;
30759
+ }
30760
+ lookupEntryUserData(member) {
30761
+ if (!member.lookup) return false;
30762
+ const collectionWritability = member.lookup.collectionWritability ?? this.project.symbolById.get(member.lookup.collectionMemberId)?.writability;
30763
+ return collectionWritability === "user";
30246
30764
  }
30247
30765
  lookupEntryWritability(member, receiver) {
30248
30766
  if (!member.lookup) return "readOnly" /* ReadOnly */;
30249
30767
  const target = this.project.symbolById.get(
30250
30768
  member.lookup.collectionMemberId
30251
30769
  );
30252
- const targetWritability = member.lookup.collectionWritability ? toWritability(member.lookup.collectionWritability) : target?.writability ? toWritability(target.writability) : target?.writable ? "runtime" /* Runtime */ : "readOnly" /* ReadOnly */;
30770
+ const targetWritability = member.lookup.collectionWritability ? toWritability(member.lookup.collectionWritability, this.writesUserData) : target?.writability ? toWritability(target.writability, this.writesUserData) : target?.writable ? "runtime" /* Runtime */ : "readOnly" /* ReadOnly */;
30253
30771
  if (receiver.writability === "immutable" /* Immutable */) {
30254
30772
  if (targetWritability === "save" /* Save */) {
30255
30773
  return "immutableToSaveLookup" /* ImmutableToSaveLookup */;
@@ -30270,7 +30788,8 @@ var init_strict_resolver = __esm({
30270
30788
  },
30271
30789
  type: optional2 ? { ...type, nullable: true } : type,
30272
30790
  ...optional2 ? { optionalChainType: type } : {},
30273
- ...receiver.writability ? { writability: receiver.writability } : {}
30791
+ ...receiver.writability ? { writability: receiver.writability } : {},
30792
+ ...receiver.userData ? { userData: true } : {}
30274
30793
  };
30275
30794
  }
30276
30795
  resolveIndex(receiverAst, indexAst, scope, pos, optional2) {
@@ -30331,7 +30850,8 @@ var init_strict_resolver = __esm({
30331
30850
  },
30332
30851
  type: nullPropagating ? { ...receiver.type.elementType, nullable: true } : receiver.type.elementType,
30333
30852
  ...nullPropagating ? { optionalChainType: receiver.type.elementType } : {},
30334
- ...receiver.writability ? { writability: receiver.writability } : {}
30853
+ ...receiver.writability ? { writability: receiver.writability } : {},
30854
+ ...receiver.userData ? { userData: true } : {}
30335
30855
  };
30336
30856
  }
30337
30857
  keyType = { kind: "primitive", name: "int" };
@@ -30365,7 +30885,8 @@ var init_strict_resolver = __esm({
30365
30885
  },
30366
30886
  type: nullPropagating ? { ...valueType, nullable: true } : valueType,
30367
30887
  ...nullPropagating ? { optionalChainType: valueType } : {},
30368
- ...receiver.writability ? { writability: receiver.writability } : {}
30888
+ ...receiver.writability ? { writability: receiver.writability } : {},
30889
+ ...receiver.userData ? { userData: true } : {}
30369
30890
  };
30370
30891
  }
30371
30892
  resolveDictionaryKey(expectedKey, keyAst, scope, pos, requireNonOptional, dictionaryLiteral = false) {
@@ -30769,6 +31290,11 @@ var init_strict_resolver = __esm({
30769
31290
  }
30770
31291
  const staticType = this.project.typeByName.get(callee.receiver.name);
30771
31292
  if (staticType && staticType.kind !== "enum") {
31293
+ this.assertUserSafe(
31294
+ { kind: "named", typeId: staticType.id },
31295
+ "a static receiver",
31296
+ pos
31297
+ );
30772
31298
  if (callee.optional) {
30773
31299
  throw new CompileError(
30774
31300
  `Optional chaining is not valid on type receiver '${staticType.name}'.`,
@@ -32917,6 +33443,9 @@ var init_strict_resolver = __esm({
32917
33443
  */
32918
33444
  resolveActionCall(action, actionType, expression, scope) {
32919
33445
  this.assertActionControlAccessible(action, "invoke", expression.pos);
33446
+ if (action.userData && action.writability === "readOnly" /* ReadOnly */) {
33447
+ throw new CompileError(USER_DATA_WRITE_ERROR, expression.pos);
33448
+ }
32920
33449
  if (expression.callee.kind === "member" && expression.callee.optional) {
32921
33450
  throw new CompileError(
32922
33451
  `${this.describe(actionType)} is never null, so optional-chained invocation is not supported.`,
@@ -33074,6 +33603,11 @@ var init_strict_resolver = __esm({
33074
33603
  expression.callee.receiver.name
33075
33604
  );
33076
33605
  if (staticType && staticType.kind !== "enum") {
33606
+ this.assertUserSafe(
33607
+ { kind: "named", typeId: staticType.id },
33608
+ "a static receiver",
33609
+ expression.pos
33610
+ );
33077
33611
  if (expression.callee.optional) {
33078
33612
  throw new CompileError(
33079
33613
  `Optional chaining is not valid on type receiver '${staticType.name}'.`,
@@ -33172,6 +33706,9 @@ var init_strict_resolver = __esm({
33172
33706
  pos
33173
33707
  );
33174
33708
  }
33709
+ if (writability === "readOnly" /* ReadOnly */ && receiver.userData) {
33710
+ throw new CompileError(USER_DATA_WRITE_ERROR, pos);
33711
+ }
33175
33712
  throw new CompileError(
33176
33713
  writability === "immutable" /* Immutable */ ? "Cannot mutate a collection whose effective storage is immutable." : "Cannot mutate a read-only collection value.",
33177
33714
  pos
@@ -33266,6 +33803,9 @@ var init_strict_resolver = __esm({
33266
33803
  pos
33267
33804
  );
33268
33805
  }
33806
+ if (writability === "readOnly" /* ReadOnly */ && receiver.userData) {
33807
+ throw new CompileError(USER_DATA_WRITE_ERROR, pos);
33808
+ }
33269
33809
  if (writability === "readOnly" /* ReadOnly */) {
33270
33810
  throw new CompileError("Cannot clear a read-only action.", pos);
33271
33811
  }
@@ -33613,7 +34153,17 @@ var init_strict_resolver = __esm({
33613
34153
  );
33614
34154
  }
33615
34155
  resolveType(type) {
33616
- return resolveNeoScriptType(type, this.context, this.project);
34156
+ const resolved = resolveNeoScriptType(type, this.context, this.project);
34157
+ this.assertUserSafe(resolved, "a type annotation", type.pos);
34158
+ return resolved;
34159
+ }
34160
+ /** P104 §3.2. User code names only User-safe types. */
34161
+ assertUserSafe(type, site, pos) {
34162
+ if (!this.userClassCode) return;
34163
+ const unsafe = userUnsafeTypeName(type, this.project);
34164
+ if (unsafe !== null) {
34165
+ throw new CompileError(userUnsafeTypeMessage(unsafe, site), pos);
34166
+ }
33617
34167
  }
33618
34168
  /**
33619
34169
  * P43 §7.1. A constructor body builds the value it is handed; every other
@@ -33779,7 +34329,12 @@ var init_strict_resolver = __esm({
33779
34329
  pointer: { type: "variable" /* Variable */, variableId: symbol.id },
33780
34330
  type: symbol.type,
33781
34331
  symbol,
33782
- ...symbol.writability ? { writability: toWritability(symbol.writability) } : {}
34332
+ ...symbol.writability ? {
34333
+ writability: toWritability(
34334
+ symbol.writability,
34335
+ this.writesUserData
34336
+ )
34337
+ } : {}
33783
34338
  };
33784
34339
  }
33785
34340
  return literal(symbol.type, null);
@@ -34151,14 +34706,16 @@ var init_project_schema_contract_generated = __esm({
34151
34706
  "Immutable",
34152
34707
  "Save",
34153
34708
  "Session",
34154
- "Writable"
34709
+ "Writable",
34710
+ "User"
34155
34711
  ],
34156
34712
  "NeoAllowedStorage": [
34157
34713
  "Any",
34158
34714
  "Immutable",
34159
34715
  "Save",
34160
34716
  "Session",
34161
- "Writable"
34717
+ "Writable",
34718
+ "User"
34162
34719
  ],
34163
34720
  "NeoListKind": [
34164
34721
  "Ordered",
@@ -37788,6 +38345,15 @@ var init_project_source_settlement = __esm({
37788
38345
  });
37789
38346
 
37790
38347
  // ../packages/neoscript-language/src/project-root.ts
38348
+ function projectRootSlotCountError(slots) {
38349
+ const predatesUser = slots.length === PROJECT_ROOT_SOURCE_SLOTS.length - 1 && slots.every(
38350
+ (slot, index) => slot.name === PROJECT_ROOT_SOURCE_SLOTS[index].name
38351
+ );
38352
+ if (predatesUser) {
38353
+ return "Root.neo predates the User root. Run `neo pull` to update Root.neo.";
38354
+ }
38355
+ return "Root must contain exactly the protected Assets, Save, Session, and User members in canonical order.";
38356
+ }
37791
38357
  function parseProjectRootInitializer(source) {
37792
38358
  if (lastRootParse?.source === source) return lastRootParse.slots;
37793
38359
  const slots = parseProjectRootEnvelope(source);
@@ -37926,7 +38492,8 @@ var init_project_root = __esm({
37926
38492
  PROJECT_ROOT_SOURCE_SLOTS = [
37927
38493
  { name: "Assets", type: "Assets", storage: "Immutable" },
37928
38494
  { name: "Save", type: "Save", storage: "Save" },
37929
- { name: "Session", type: "Session", storage: "Session" }
38495
+ { name: "Session", type: "Session", storage: "Session" },
38496
+ { name: "User", type: "User", storage: "User" }
37930
38497
  ];
37931
38498
  }
37932
38499
  });
@@ -38237,8 +38804,28 @@ function validateDeclaration(uri, declaration, documentKind, environment, diagno
38237
38804
  }
38238
38805
  if (declaration.kind === "class") {
38239
38806
  validateRetiredParameterReads(uri, declaration, scope, diagnostics);
38807
+ validateUserClassSignatures(uri, declaration, environment, diagnostics);
38240
38808
  }
38241
38809
  }
38810
+ function validateUserClassSignatures(uri, declaration, environment, diagnostics) {
38811
+ const type = environment.projectIndex.typeByName.get(declaration.name);
38812
+ if (type?.userClass !== true) return;
38813
+ const violation = userClassSignatureViolation(type, environment.projectIndex);
38814
+ if (violation === null) return;
38815
+ const member = declaration.members.find(
38816
+ (candidate) => candidate.name === violation.memberName
38817
+ );
38818
+ pushDiagnostic(
38819
+ diagnostics,
38820
+ uri,
38821
+ member?.nameRange ?? declaration.nameRange,
38822
+ "user-unsafe-type",
38823
+ userUnsafeTypeMessage(
38824
+ violation.typeName,
38825
+ violation.memberName === null ? `a constructor of \`${declaration.name}\`` : `\`${declaration.name}.${violation.memberName}\``
38826
+ )
38827
+ );
38828
+ }
38242
38829
  function validateRetiredParameterReads(uri, declaration, scope, diagnostics) {
38243
38830
  if (declaration.headerParameters !== void 0) return;
38244
38831
  const retired = /* @__PURE__ */ new Set();
@@ -38470,7 +39057,7 @@ function validateProjectRootGlobal(uri, declaration, environment, diagnostics) {
38470
39057
  uri,
38471
39058
  declaration.initializer.range,
38472
39059
  "protected-root-envelope",
38473
- "Root must contain exactly the protected Assets, Save, and Session members in canonical order."
39060
+ projectRootSlotCountError(slots)
38474
39061
  );
38475
39062
  return;
38476
39063
  }
@@ -40740,6 +41327,7 @@ var init_project_source_semantics = __esm({
40740
41327
  "../packages/neoscript-language/src/project-source-semantics.ts"() {
40741
41328
  "use strict";
40742
41329
  init_project();
41330
+ init_user_safe_types();
40743
41331
  init_strict_resolver();
40744
41332
  init_reference_argument();
40745
41333
  init_project_source_ast();
@@ -42375,45 +42963,12 @@ var init_project_source_script_compiler = __esm({
42375
42963
  PRIMITIVE_NAMES = new Set(PRIMITIVE_NAME_LIST);
42376
42964
  STORAGE_WRITABILITY = {
42377
42965
  Save: "save",
42378
- Session: "session"
42966
+ Session: "session",
42967
+ User: "user"
42379
42968
  };
42380
42969
  }
42381
42970
  });
42382
42971
 
42383
- // ../packages/neoscript-language/src/project-source-lifecycle-hooks.ts
42384
- function isScriptRuntimeInterface(declaration) {
42385
- const kind = declaration.annotations.find((annotation2) => annotation2.name === "system")?.arguments.find((argument2) => argument2.name === "kind")?.text.trim();
42386
- return kind === ".ScriptRuntime";
42387
- }
42388
- function lifecycleHooksByMemberName(interfaces) {
42389
- const hooks = /* @__PURE__ */ new Map();
42390
- for (const declaration of interfaces) {
42391
- if (!isScriptRuntimeInterface(declaration)) continue;
42392
- for (const member of declaration.members) {
42393
- if (member.kind !== "function") continue;
42394
- hooks.set(member.name.toLowerCase(), {
42395
- interfaceName: declaration.name,
42396
- member,
42397
- ...declaration.docsText === void 0 ? {} : { docsText: declaration.docsText },
42398
- unity: declaration.name.startsWith(UNITY_LIFECYCLE_INTERFACE_PREFIX)
42399
- });
42400
- }
42401
- }
42402
- return hooks;
42403
- }
42404
- function reservedLifecycleMemberMessage(memberName, interfaceName, owner) {
42405
- const fix = owner === "class" ? "Implement the interface with a public method" : "Extend the interface";
42406
- return `'${memberName}' is reserved for '${interfaceName}'. ${fix}, or rename the member.`;
42407
- }
42408
- var NEO_LIFECYCLE_OBJECT_CLASS_NAME, UNITY_LIFECYCLE_INTERFACE_PREFIX;
42409
- var init_project_source_lifecycle_hooks = __esm({
42410
- "../packages/neoscript-language/src/project-source-lifecycle-hooks.ts"() {
42411
- "use strict";
42412
- NEO_LIFECYCLE_OBJECT_CLASS_NAME = "NeoObjectBase";
42413
- UNITY_LIFECYCLE_INTERFACE_PREFIX = "INeoUnity";
42414
- }
42415
- });
42416
-
42417
42972
  // ../packages/neoscript-language/src/project-source-type-checker.ts
42418
42973
  function validateProjectSourceTypes(documents) {
42419
42974
  const diagnostics = [];
@@ -44729,8 +45284,8 @@ function collectFlowSymbols(uri, content, ownerName, scopeRange, symbols, diagno
44729
45284
  }
44730
45285
  }
44731
45286
  function collectSymbol(uri, kind, name, range2, annotations, ownerName, symbols, diagnostics, scopeRange, detail, callable2, documentation, staticMember) {
44732
- const memberLabel2 = kind === "member" || kind === "enumOption" || kind === "graphChild";
44733
- const reserved = memberLabel2 ? isNeoProjectMemberReservedName(name) : isNeoProjectReservedName(name);
45287
+ const memberLabel3 = kind === "member" || kind === "enumOption" || kind === "graphChild";
45288
+ const reserved = memberLabel3 ? isNeoProjectMemberReservedName(name) : isNeoProjectReservedName(name);
44734
45289
  if (reserved) {
44735
45290
  diagnostics.push({
44736
45291
  uri,
@@ -46198,7 +46753,7 @@ var init_registry = __esm({
46198
46753
  "../packages/manifest/schema-contract/registry.mjs"() {
46199
46754
  "use strict";
46200
46755
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
46201
- cliVersion: "0.71.4",
46756
+ cliVersion: "0.72.0",
46202
46757
  projectFileUploadBatchSize: 32,
46203
46758
  documentRecords: {
46204
46759
  member: {
@@ -46544,8 +47099,22 @@ var init_registry = __esm({
46544
47099
  sdkDeclarations: {
46545
47100
  attributes: {
46546
47101
  enums: {
46547
- NeoStorage: ["Inherit", "Immutable", "Save", "Session", "Writable"],
46548
- NeoAllowedStorage: ["Any", "Immutable", "Save", "Session", "Writable"],
47102
+ NeoStorage: [
47103
+ "Inherit",
47104
+ "Immutable",
47105
+ "Save",
47106
+ "Session",
47107
+ "Writable",
47108
+ "User"
47109
+ ],
47110
+ NeoAllowedStorage: [
47111
+ "Any",
47112
+ "Immutable",
47113
+ "Save",
47114
+ "Session",
47115
+ "Writable",
47116
+ "User"
47117
+ ],
46549
47118
  NeoListKind: ["Ordered", "Unordered"],
46550
47119
  NeoDictionaryKeyKind: ["String", "Enum"],
46551
47120
  NeoClassVisibility: ["Visible", "Hidden"],
@@ -50655,7 +51224,9 @@ var init_schema_member = __esm({
50655
51224
  defaultValue: nullable(schemaDefaultValueSchema),
50656
51225
  overrideOf: nullableId,
50657
51226
  system: nullable(schemaSystemMetadataSchema),
50658
- storage: nullable(picklist(["immutable", "save", "session", "writable"])),
51227
+ storage: nullable(
51228
+ picklist(["immutable", "save", "session", "writable", "user"])
51229
+ ),
50659
51230
  distribution: nullable(picklist(["sparse", "packed"])),
50660
51231
  storageKey: nullableId
50661
51232
  };
@@ -52346,7 +52917,8 @@ var init_codecs = __esm({
52346
52917
  "immutable",
52347
52918
  "save",
52348
52919
  "session",
52349
- "writable"
52920
+ "writable",
52921
+ "user"
52350
52922
  ];
52351
52923
  MEMBER_DISTRIBUTION = ["sparse", "packed"];
52352
52924
  STRING_FORMAT = ["localized", "plain"];
@@ -53148,7 +53720,8 @@ var init_class = __esm({
53148
53720
  "immutable",
53149
53721
  "save",
53150
53722
  "session",
53151
- "writable"
53723
+ "writable",
53724
+ "user"
53152
53725
  ];
53153
53726
  CLASS_SCHEMA_RECORD_ADAPTER = {
53154
53727
  recordKind: "class",
@@ -55204,7 +55777,11 @@ function booleanAt(value, path) {
55204
55777
  }
55205
55778
  function nullableStorage(value, path) {
55206
55779
  if (value === null) return;
55207
- stringIn(value, /* @__PURE__ */ new Set(["immutable", "save", "session", "writable"]), path);
55780
+ stringIn(
55781
+ value,
55782
+ /* @__PURE__ */ new Set(["immutable", "save", "session", "writable", "user"]),
55783
+ path
55784
+ );
55208
55785
  }
55209
55786
  function stringIn(value, allowed, path) {
55210
55787
  const result = stringAt(value, path);
@@ -55587,16 +56164,8 @@ function quoteNeoString(value) {
55587
56164
  return JSON.stringify(value);
55588
56165
  }
55589
56166
  function indentNeoSource(value, spaces) {
55590
- return indentLines(value, spaces, true);
55591
- }
55592
- function indentNeoSourceNonEmptyLines(value, spaces) {
55593
- return indentLines(value, spaces, false);
55594
- }
55595
- function indentLines(value, spaces, indentEmptyLines) {
55596
56167
  const prefix = " ".repeat(spaces);
55597
- return value.split("\n").map(
55598
- (line) => indentEmptyLines || line.length > 0 ? `${prefix}${line}` : line
55599
- ).join("\n");
56168
+ return value.split("\n").map((line) => line.length > 0 ? `${prefix}${line}` : line).join("\n");
55600
56169
  }
55601
56170
  function formatNeoNumber(value) {
55602
56171
  if (!Number.isFinite(value)) {
@@ -56553,12 +57122,12 @@ ${schemaClass2.genericParameters.map((parameter4) => {
56553
57122
  const rendered = memberIds.map((memberId) => {
56554
57123
  const member = required(context.members, memberId, "member");
56555
57124
  const alternateIndex = context.formAlternates.length;
56556
- const source = indentNeoSourceNonEmptyLines(emitMember(context, member, schemaClass2.id), 2);
57125
+ const source = indentNeoSource(emitMember(context, member, schemaClass2.id), 2);
56557
57126
  for (let at3 = alternateIndex; at3 < context.formAlternates.length; at3++) {
56558
57127
  const alternate = context.formAlternates[at3];
56559
57128
  context.formAlternates[at3] = {
56560
- shorthand: indentNeoSourceNonEmptyLines(alternate.shorthand, 2),
56561
- block: indentNeoSourceNonEmptyLines(alternate.block, 2)
57129
+ shorthand: indentNeoSource(alternate.shorthand, 2),
57130
+ block: indentNeoSource(alternate.block, 2)
56562
57131
  };
56563
57132
  }
56564
57133
  return { member, source };
@@ -56567,7 +57136,7 @@ ${schemaClass2.genericParameters.map((parameter4) => {
56567
57136
  const sources2 = rendered.map((entry) => entry.source);
56568
57137
  for (const memberId of memberIds)
56569
57138
  for (const statement of listenerStatementsByMember?.get(memberId) ?? [])
56570
- sources2.push(indentNeoSourceNonEmptyLines(statement, 2));
57139
+ sources2.push(indentNeoSource(statement, 2));
56571
57140
  const spliced = [
56572
57141
  ...constructorIds.map(
56573
57142
  (constructorId) => emitConstructor(
@@ -56591,7 +57160,7 @@ ${sources2.join("\n\n")}
56591
57160
  );
56592
57161
  const members = [
56593
57162
  ...sources2.slice(0, lastValueMemberIndex + 1),
56594
- ...spliced.map((source) => indentNeoSourceNonEmptyLines(source, 2)),
57163
+ ...spliced.map((source) => indentNeoSource(source, 2)),
56595
57164
  ...sources2.slice(lastValueMemberIndex + 1)
56596
57165
  ];
56597
57166
  return `${emitDocsText(schemaClass2.docsText)}${annotations.join("\n")}
@@ -56679,7 +57248,7 @@ function renderParameterDefault(context, argument2) {
56679
57248
  function emitInitBlock(code) {
56680
57249
  if (code.trim().length === 0) return "init {\n}";
56681
57250
  return `init {
56682
- ${indentNeoSourceNonEmptyLines(code.trim(), 2)}
57251
+ ${indentNeoSource(code.trim(), 2)}
56683
57252
  }`;
56684
57253
  }
56685
57254
  function isCallableMember(member) {
@@ -56695,7 +57264,7 @@ function emitConstructor(context, schemaClass2, declared) {
56695
57264
  : base(${declared.baseArguments.map((argument2) => `${argument2.name}: ${argument2.code}`).join(", ")})`;
56696
57265
  const code = declared.code ?? "";
56697
57266
  const body = code.trim().length === 0 ? "{\n}" : `{
56698
- ${indentNeoSourceNonEmptyLines(code.trim(), 2)}
57267
+ ${indentNeoSource(code.trim(), 2)}
56699
57268
  }`;
56700
57269
  const signature = `public ${schemaClass2.name}(${parameters})`;
56701
57270
  return `${annotations.join("\n")}
@@ -56740,10 +57309,10 @@ ${expression}`;
56740
57309
  );
56741
57310
  }
56742
57311
  return ` ${property2}:
56743
- ${indentNeoSourceNonEmptyLines(render(ordered[0]), 4)}`;
57312
+ ${indentNeoSource(render(ordered[0]), 4)}`;
56744
57313
  }
56745
57314
  return ` ${property2}: [
56746
- ${ordered.map((relation) => `${indentNeoSourceNonEmptyLines(render(relation), 4)},`).join("\n\n")}
57315
+ ${ordered.map((relation) => `${indentNeoSource(render(relation), 4)},`).join("\n\n")}
56747
57316
  ]`;
56748
57317
  });
56749
57318
  return [`@relations(
@@ -56765,7 +57334,7 @@ function emitInterface(context, value) {
56765
57334
  const system = value.system ? `${systemAnnotation2(value.system)}
56766
57335
  ` : "";
56767
57336
  return `${emitDocsText(value.docsText)}${id(value.id)}${system}interface ${value.name}${bases.length ? ` : ${bases.join(", ")}` : ""} {
56768
- ${members.map((member) => indentNeoSourceNonEmptyLines(member, 2)).join("\n\n")}
57337
+ ${members.map((member) => indentNeoSource(member, 2)).join("\n\n")}
56769
57338
  }
56770
57339
  `;
56771
57340
  }
@@ -56774,7 +57343,7 @@ function emitEnum(value) {
56774
57343
  (option) => `${emitDocsText(option.docsText)}${id(option.id)}${option.name}${option.text === option.name ? "" : ` = ${quote(option.text)}`},`
56775
57344
  );
56776
57345
  return `${emitDocsText(value.docsText)}${id(value.id)}enum ${value.name} {
56777
- ${options.map((option) => indentNeoSourceNonEmptyLines(option, 2)).join("\n")}
57346
+ ${options.map((option) => indentNeoSource(option, 2)).join("\n")}
56778
57347
  }
56779
57348
  `;
56780
57349
  }
@@ -56807,11 +57376,11 @@ ${prefix}${renderType(context, member.returnType, enclosingClassId)} ${member.na
56807
57376
  const setterBlock = setter ? `
56808
57377
 
56809
57378
  set {
56810
- ${indentNeoSourceNonEmptyLines(setter, 4)}
57379
+ ${indentNeoSource(setter, 4)}
56811
57380
  }` : "";
56812
57381
  const block = `${head} {
56813
57382
  get {
56814
- ${indentNeoSourceNonEmptyLines(getter, 4)}
57383
+ ${indentNeoSource(getter, 4)}
56815
57384
  }${setterBlock}
56816
57385
  }`;
56817
57386
  const expression = expressionBodyExpression(getter);
@@ -57791,7 +58360,7 @@ function emitFunctionBody(sourceText) {
57791
58360
  const code = sourceText.trim();
57792
58361
  if (code.length === 0) return "{\n}";
57793
58362
  return `{
57794
- ${indentNeoSourceNonEmptyLines(code, 2)}
58363
+ ${indentNeoSource(code, 2)}
57795
58364
  }`;
57796
58365
  }
57797
58366
  function id(value) {
@@ -57910,7 +58479,7 @@ function emitVariant(context, className, variant2, folder, variantInitializers)
57910
58479
  const type = folder?.binding == null ? `NeoVariant<${className}>` : `${NEO_LOOKUP_VARIANT_TYPE_NAME}<${className}, ${renderLookupEntryType(context, folder.binding.collectionMemberId)}>`;
57911
58480
  return `${annotations.join("\n")}
57912
58481
  ${type} ${variant2.name} =
57913
- ${indentNeoSourceNonEmptyLines(initializer, 2)};`;
58482
+ ${indentNeoSource(initializer, 2)};`;
57914
58483
  }
57915
58484
  function variantFolderSymbol(folder) {
57916
58485
  if (folder.path.length === 0) return "Root";
@@ -62084,7 +62653,7 @@ function numberArgument(value, name) {
62084
62653
  }
62085
62654
  function storageValue(value, name) {
62086
62655
  const enumValue2 = contextualEnumArgument(value, name)?.toLowerCase();
62087
- return enumValue2 === "immutable" || enumValue2 === "save" || enumValue2 === "session" || enumValue2 === "writable" ? enumValue2 : null;
62656
+ return enumValue2 === "immutable" || enumValue2 === "save" || enumValue2 === "session" || enumValue2 === "writable" || enumValue2 === "user" ? enumValue2 : null;
62088
62657
  }
62089
62658
  function assertWritableStorageRules(context, ownerClass, declaration, inheritedId) {
62090
62659
  const site = `${ownerClass.name}.${declaration.name}`;
@@ -65801,7 +66370,8 @@ var init_dialogue_sources = __esm({
65801
66370
  for (const [field, expression] of [
65802
66371
  ["rootAssetsMemberId", "root.Assets"],
65803
66372
  ["rootSaveFileMemberId", "root.Save"],
65804
- ["rootSessionMemberId", "root.Session"]
66373
+ ["rootSessionMemberId", "root.Session"],
66374
+ ["rootUserMemberId", "root.User"]
65805
66375
  ]) {
65806
66376
  const memberId = optionalString2(project[field]);
65807
66377
  const member = memberId ? this.members.get(memberId) : void 0;
@@ -66937,7 +67507,7 @@ function projectSchemaManifest(input, options = {}) {
66937
67507
  genericOwnerClassIds.set(parameterId, classId);
66938
67508
  }
66939
67509
  }
66940
- const rootMemberIds2 = options.rootMemberIds ?? inferRootMemberIds(members);
67510
+ const rootMemberIds = options.rootMemberIds ?? inferRootMemberIds(members);
66941
67511
  const environment = {
66942
67512
  sourceUri: options.sourceUri ?? defaultSourceUri,
66943
67513
  members: memberById2,
@@ -66954,7 +67524,7 @@ function projectSchemaManifest(input, options = {}) {
66954
67524
  effectiveWritabilityByMemberId: effectiveMemberWritability(
66955
67525
  members,
66956
67526
  classes,
66957
- rootMemberIds2
67527
+ rootMemberIds
66958
67528
  )
66959
67529
  };
66960
67530
  const declaredTypes = [
@@ -66968,7 +67538,7 @@ function projectSchemaManifest(input, options = {}) {
66968
67538
  (item, index) => enumType(item, environment, `enums[${index}]`)
66969
67539
  )
66970
67540
  ];
66971
- const rootType = createRootType(rootMemberIds2, environment);
67541
+ const rootType = createRootType(rootMemberIds, environment);
66972
67542
  declaredTypes.push(rootType);
66973
67543
  const roots = options.roots ?? [
66974
67544
  {
@@ -67107,10 +67677,10 @@ function resolvedAllowedStorage(classId, environment) {
67107
67677
  return null;
67108
67678
  }
67109
67679
  function constraintDefaultWritability(allowed) {
67110
- return allowed === "immutable" || allowed === "save" || allowed === "session" ? allowed : "session";
67680
+ return allowed === "immutable" || allowed === "save" || allowed === "session" || allowed === "user" ? allowed : "session";
67111
67681
  }
67112
67682
  function declaredStorageWritability(storage, staticOwnerClassId, environment) {
67113
- if (storage === "immutable" || storage === "save" || storage === "session") {
67683
+ if (storage === "immutable" || storage === "save" || storage === "session" || storage === "user") {
67114
67684
  return storage;
67115
67685
  }
67116
67686
  if (storage === "writable") return "runtime";
@@ -67124,12 +67694,13 @@ function staticDefaultWritability(classId, environment) {
67124
67694
  }
67125
67695
  function createRootType(ids, environment) {
67126
67696
  const definitions = [
67127
- ["Assets", ids.assets, false],
67128
- ["Save", ids.save, true],
67129
- ["Session", ids.session, true]
67697
+ ["Assets", ids.assets, false, "immutable"],
67698
+ ["Save", ids.save, true, "save"],
67699
+ ["Session", ids.session, true, "session"],
67700
+ ["User", ids.user, true, "user"]
67130
67701
  ];
67131
67702
  const members = [];
67132
- for (const [name, memberId, writable] of definitions) {
67703
+ for (const [name, memberId, writable, writability] of definitions) {
67133
67704
  if (!memberId) continue;
67134
67705
  const member = environment.members.get(memberId);
67135
67706
  if (!member) continue;
@@ -67139,7 +67710,7 @@ function createRootType(ids, environment) {
67139
67710
  kind: "property",
67140
67711
  type: memberType(member, environment, /* @__PURE__ */ new Set(), `root.${name}`),
67141
67712
  writable,
67142
- writability: name === "Assets" ? "immutable" : name === "Session" ? "session" : "save",
67713
+ writability,
67143
67714
  documentation: `Project ${name.toLowerCase()} root (${memberId}).`,
67144
67715
  ...sourceLocation(member.source, environment, `root.${name}.source`)
67145
67716
  });
@@ -67162,7 +67733,8 @@ function inferRootMemberIds(members) {
67162
67733
  return {
67163
67734
  assets: unique("immutable"),
67164
67735
  save: unique("save"),
67165
- session: unique("session")
67736
+ session: unique("session"),
67737
+ user: unique("user")
67166
67738
  };
67167
67739
  }
67168
67740
  function effectiveMemberWritability(members, classes, roots) {
@@ -67183,7 +67755,7 @@ function effectiveMemberWritability(members, classes, roots) {
67183
67755
  seen.add(memberId);
67184
67756
  const member = memberById2.get(memberId);
67185
67757
  if (!member) return null;
67186
- if (member.storage === "immutable" || member.storage === "save" || member.storage === "session" || member.storage === "writable") {
67758
+ if (member.storage === "immutable" || member.storage === "save" || member.storage === "session" || member.storage === "writable" || member.storage === "user") {
67187
67759
  return member.storage;
67188
67760
  }
67189
67761
  return typeof member.overrideOf === "string" ? declaredStorage(member.overrideOf, seen) : null;
@@ -67251,6 +67823,7 @@ function effectiveMemberWritability(members, classes, roots) {
67251
67823
  if (typeof roots.session === "string") {
67252
67824
  rootStorage.set(roots.session, "session");
67253
67825
  }
67826
+ if (typeof roots.user === "string") rootStorage.set(roots.user, "user");
67254
67827
  for (const [memberId, member] of memberById2) {
67255
67828
  const declared = rootStorage.get(memberId) ?? declaredStorage(memberId);
67256
67829
  if (declared === "writable") {
@@ -67322,6 +67895,7 @@ function schemaClass(value, environment, field) {
67322
67895
  ),
67323
67896
  ...value.declarationModifier === "abstract" ? { abstract: true } : {},
67324
67897
  ...value.declarationModifier === "sealed" ? { sealed: true } : {},
67898
+ ...resolvedAllowedStorage(id2, environment) === "user" ? { userClass: true } : {},
67325
67899
  ...sourceLocation(value.source, environment, `${field}.source`),
67326
67900
  typeParameters: records(
67327
67901
  value.genericParameters ?? [],
@@ -68817,62 +69391,6 @@ var init_variant_member = __esm({
68817
69391
  }
68818
69392
  });
68819
69393
 
68820
- // ../src/models/project/project-root-members.ts
68821
- function buildRootStorageMap(project) {
68822
- const map = /* @__PURE__ */ new Map();
68823
- for (const root of PROJECT_ROOTS) {
68824
- map.set(project[root.projectField], root.storage);
68825
- }
68826
- return map;
68827
- }
68828
- function writtenRootStorage(project, memberIds) {
68829
- const map = /* @__PURE__ */ new Map();
68830
- for (const root of PROJECT_ROOTS) {
68831
- const memberId = project[root.projectField];
68832
- if (memberIds.has(memberId)) map.set(memberId, root.storage);
68833
- }
68834
- return map;
68835
- }
68836
- function isProjectRootMember(project, memberId) {
68837
- return PROJECT_ROOTS.some((root) => project[root.projectField] === memberId);
68838
- }
68839
- function projectRootMembersInDisplayOrder(project) {
68840
- return PROJECT_ROOTS.map((root) => ({
68841
- root,
68842
- memberId: project[root.projectField]
68843
- }));
68844
- }
68845
- var PROJECT_ROOTS;
68846
- var init_project_root_members = __esm({
68847
- "../src/models/project/project-root-members.ts"() {
68848
- "use strict";
68849
- init_member_storage();
68850
- PROJECT_ROOTS = [
68851
- {
68852
- key: "Assets",
68853
- choice: "assets",
68854
- projectField: "rootAssetsMemberId",
68855
- label: "Assets",
68856
- storage: 1 /* Immutable */
68857
- },
68858
- {
68859
- key: "Save",
68860
- choice: "save",
68861
- projectField: "rootSaveFileMemberId",
68862
- label: "Save",
68863
- storage: 2 /* Save */
68864
- },
68865
- {
68866
- key: "Session",
68867
- choice: "session",
68868
- projectField: "rootSessionMemberId",
68869
- label: "Session",
68870
- storage: 3 /* Session */
68871
- }
68872
- ];
68873
- }
68874
- });
68875
-
68876
69394
  // ../src/runtime/neoscript/analyzer-types.ts
68877
69395
  function createAnalyzerSchemaIndex(context) {
68878
69396
  return projectSchemaIndexFor({
@@ -69181,6 +69699,7 @@ function classToLanguageType(schemaClass2, context) {
69181
69699
  ...schemaClass2.docsText ? { documentation: schemaClass2.docsText } : {},
69182
69700
  kind: "class",
69183
69701
  abstract: isAbstractClass(schemaClass2),
69702
+ ...resolveAllowedStorage(schemaClass2.id, context.vm.classes) === 5 /* User */ ? { userClass: true } : {},
69184
69703
  location: virtualIdentifierLocation(
69185
69704
  context,
69186
69705
  "Classes",
@@ -69455,6 +69974,8 @@ function symbolWritability(memberId, context, staticOwnerClassId) {
69455
69974
  return { writability: "session" };
69456
69975
  case 4 /* Writable */:
69457
69976
  return { writability: "runtime" };
69977
+ case 5 /* User */:
69978
+ return { writability: "user" };
69458
69979
  }
69459
69980
  }
69460
69981
  function uniqueEffectiveWritability(memberId, context) {
@@ -69476,6 +69997,8 @@ function uniqueEffectiveWritability(memberId, context) {
69476
69997
  return "save";
69477
69998
  case 3 /* Session */:
69478
69999
  return "session";
70000
+ case 5 /* User */:
70001
+ return "user";
69479
70002
  }
69480
70003
  }
69481
70004
  function interfaceToLanguageType(neoInterface, context) {
@@ -69576,13 +70099,10 @@ function interfaceMemberToSymbol(neoInterface, key, member, context, inherited)
69576
70099
  };
69577
70100
  }
69578
70101
  function createRootType2(context) {
69579
- const definitions = [
69580
- ["Assets", context.vm.project.rootAssetsMemberId],
69581
- ["Save", context.vm.project.rootSaveFileMemberId],
69582
- ["Session", context.vm.project.rootSessionMemberId]
69583
- ];
69584
70102
  const members = [];
69585
- for (const [index, [name, memberId]] of definitions.entries()) {
70103
+ for (const [index, root] of PROJECT_ROOTS.entries()) {
70104
+ const name = root.key;
70105
+ const memberId = context.vm.project[root.projectField];
69586
70106
  const member = analyzerMemberById(context, memberId);
69587
70107
  if (!member) continue;
69588
70108
  members.push({
@@ -69590,8 +70110,8 @@ function createRootType2(context) {
69590
70110
  name,
69591
70111
  kind: "property",
69592
70112
  type: memberRuntimeType(member, context),
69593
- writable: name !== "Assets",
69594
- writability: name === "Assets" ? "immutable" : name === "Session" ? "session" : "save",
70113
+ writable: root.storage !== 1 /* Immutable */,
70114
+ writability: ROOT_WRITABILITY[root.storage],
69595
70115
  documentation: `Project ${name.toLowerCase()} root (${memberId}).`,
69596
70116
  location: virtualIdentifierLocation(
69597
70117
  context,
@@ -70158,9 +70678,7 @@ function projectLanguageVersion(context) {
70158
70678
  `adapter:${NEOSCRIPT_COMPILER_ADAPTER_REVISION}`,
70159
70679
  context.vm.project.id,
70160
70680
  context.vm.project.updatedAt,
70161
- context.vm.project.rootAssetsMemberId,
70162
- context.vm.project.rootSaveFileMemberId,
70163
- context.vm.project.rootSessionMemberId,
70681
+ ...projectRootMemberIds(context.vm.project),
70164
70682
  ...records2.map((record7) => `${record7.id}:${record7.updatedAt}`).sort()
70165
70683
  ].join("|");
70166
70684
  }
@@ -70344,7 +70862,7 @@ function virtualEnumOptionLine(optionName, optionId = optionName) {
70344
70862
  function virtualRootMemberLine(name, memberId) {
70345
70863
  return ` public object ${virtualCSharpIdentifier(name)} { get; } // Neo root member ${memberId}`;
70346
70864
  }
70347
- var NEOSCRIPT_COMPILER_ADAPTER_REVISION, UNKNOWN_TYPE2, storageResolverByContext, CANONICAL_MEMBER_IDS;
70865
+ var NEOSCRIPT_COMPILER_ADAPTER_REVISION, UNKNOWN_TYPE2, storageResolverByContext, CANONICAL_MEMBER_IDS, ROOT_WRITABILITY;
70348
70866
  var init_neoscript_language_context_adapter = __esm({
70349
70867
  "../src/runtime/neoscript/neoscript-language-context-adapter.ts"() {
70350
70868
  "use strict";
@@ -70370,6 +70888,12 @@ var init_neoscript_language_context_adapter = __esm({
70370
70888
  };
70371
70889
  storageResolverByContext = /* @__PURE__ */ new WeakMap();
70372
70890
  CANONICAL_MEMBER_IDS = /* @__PURE__ */ new WeakMap();
70891
+ ROOT_WRITABILITY = {
70892
+ [1 /* Immutable */]: "immutable",
70893
+ [2 /* Save */]: "save",
70894
+ [3 /* Session */]: "session",
70895
+ [5 /* User */]: "user"
70896
+ };
70373
70897
  }
70374
70898
  });
70375
70899
 
@@ -75858,6 +76382,15 @@ function readNeoScriptRuntimeRow(ctx, valueId) {
75858
76382
  ctx.__valueOverlay
75859
76383
  );
75860
76384
  }
76385
+ function inspectNeoScriptValueReference(ctx, value, collection, index) {
76386
+ let id2 = findKnownRowIdByValueReference(value, ctx);
76387
+ if (id2 === null && collection !== void 0 && index !== void 0 && typeof value === "string" && (findKnownRowIdByValueReference(collection, ctx) !== null || Array.isArray(collection) && typeof index === "number" && ctx.__unorderedListProjections?.referenceEntryIndexesByArray.get(collection)?.has(index) || typeof collection === "object" && collection !== null && typeof index === "string" && ctx.__unorderedListProjections?.referenceEntryKeysByRecord.get(collection)?.has(index))) {
76388
+ id2 = value;
76389
+ }
76390
+ if (id2 === null) return null;
76391
+ const row = readNeoScriptRuntimeRow(ctx, id2);
76392
+ return row === null ? null : { row, member: memberForValueRow(row, ctx) };
76393
+ }
75861
76394
  function withEvaluationRuntime(ctx, writes) {
75862
76395
  if (ctx.__runtimeSessionValues !== void 0 && ctx.__executionState !== void 0 && ctx.__valueOverlay !== void 0) {
75863
76396
  ctx.__unorderedListProjections ??= createUnorderedListProjectionCache();
@@ -78228,6 +78761,11 @@ function effectiveStaticStorage(memberId, ctx) {
78228
78761
  `Static member '${memberId}' did not resolve to exactly one effective storage class.`
78229
78762
  );
78230
78763
  }
78764
+ if (storage === 5 /* User */) {
78765
+ throw new NSGetterRuntimeError(
78766
+ `Static member '${memberId}' belongs to a User class. User classes can't declare static stored members.`
78767
+ );
78768
+ }
78231
78769
  return storage;
78232
78770
  }
78233
78771
  function staticBindingTargetId(member, ctx) {
@@ -78980,6 +79518,10 @@ function listenerReceiverOnOwner(memberId, ownerReceiver, ctx) {
78980
79518
  return ownerReceiver;
78981
79519
  }
78982
79520
  function invokeActionListeners(listeners, args, ctx, frame, ownerReceiver, callSiteId) {
79521
+ if (ctx.assertActionInvocable !== void 0) {
79522
+ const ownerRowId = findKnownRowIdByValueReference(ownerReceiver, ctx);
79523
+ if (ownerRowId !== null) ctx.assertActionInvocable(ownerRowId);
79524
+ }
78983
79525
  for (let index = 0; index < listeners.length; index += 1) {
78984
79526
  try {
78985
79527
  invokeDelegateValue(
@@ -80644,17 +81186,9 @@ function classIdForValueRow(row, ctx) {
80644
81186
  function rootMemberForValueRow(row, ctx) {
80645
81187
  if (typeof ctx.rootValue !== "object" || ctx.rootValue === null) return null;
80646
81188
  const rootRecord = ctx.rootValue;
80647
- const project = ctx.vm.project;
80648
- if (rootRecord.Assets === row.value) {
80649
- return evalMemberById(ctx.vm, project.rootAssetsMemberId);
80650
- }
80651
- if (rootRecord.Save === row.value) {
80652
- return evalMemberById(ctx.vm, project.rootSaveFileMemberId);
80653
- }
80654
- if (rootRecord.Session === row.value) {
80655
- return evalMemberById(ctx.vm, project.rootSessionMemberId);
80656
- }
80657
- return null;
81189
+ const root = PROJECT_ROOTS.find((root2) => rootRecord[root2.key] === row.value);
81190
+ if (root === void 0) return null;
81191
+ return evalMemberById(ctx.vm, ctx.vm.project[root.projectField]);
80658
81192
  }
80659
81193
  function memberForCustomSchemaValue(classId, schemaKey, ctx) {
80660
81194
  const memberId = cachedMergedSchemaMemberId(classId, schemaKey, ctx);
@@ -91122,7 +91656,7 @@ function collectVariantGraphValueIds(args) {
91122
91656
  args.variants.filter((variant2) => args.variantIds.has(variant2.id))
91123
91657
  );
91124
91658
  if (roots.length === 0) return collected;
91125
- const rootMemberIds2 = new Set(roots.map((root) => root.member.id));
91659
+ const rootMemberIds = new Set(roots.map((root) => root.member.id));
91126
91660
  const rootOwners = /* @__PURE__ */ new Map();
91127
91661
  resolveSemanticOwnerMembersForValues(
91128
91662
  document,
@@ -91131,7 +91665,7 @@ function collectVariantGraphValueIds(args) {
91131
91665
  rootOwners
91132
91666
  );
91133
91667
  for (const [valueId, rootOwner] of rootOwners) {
91134
- if (!rootMemberIds2.has(rootOwner.id)) continue;
91668
+ if (!rootMemberIds.has(rootOwner.id)) continue;
91135
91669
  collected.add(valueId);
91136
91670
  }
91137
91671
  return collected;
@@ -93096,7 +93630,7 @@ var init_project_read_base = __esm({
93096
93630
  // ../src/models/project/project-types.ts
93097
93631
  function isProject(value) {
93098
93632
  const v = value;
93099
- return typeof v?.name === "string" && typeof v?.id === "string" && isProjectOwner(v.owner) && isActorIdentifier(v.author) && typeof v?.createdAt === "number" && typeof v?.updatedAt === "number" && typeof v?.rootAssetsMemberId === "string" && typeof v?.rootSaveFileMemberId === "string" && typeof v?.rootSessionMemberId === "string" && isOptionalNullableString4(v.defaultPriorityGroupId) && isOptionalNullableString4(v.defaultTextureTemplateId) && isOptionalNullableString4(v.defaultAudioClipTemplateId) && isProjectExportSettingsOrEmpty(v.exportSettings);
93633
+ return typeof v?.name === "string" && typeof v?.id === "string" && isProjectOwner(v.owner) && isActorIdentifier(v.author) && typeof v?.createdAt === "number" && typeof v?.updatedAt === "number" && typeof v?.rootAssetsMemberId === "string" && typeof v?.rootSaveFileMemberId === "string" && typeof v?.rootSessionMemberId === "string" && typeof v?.rootUserMemberId === "string" && isOptionalNullableString4(v.defaultPriorityGroupId) && isOptionalNullableString4(v.defaultTextureTemplateId) && isOptionalNullableString4(v.defaultAudioClipTemplateId) && isProjectExportSettingsOrEmpty(v.exportSettings);
93100
93634
  }
93101
93635
  function isProjectOwner(value) {
93102
93636
  if (!isActorIdentifier(value)) return false;
@@ -97471,10 +98005,10 @@ function commitPreparationAnimationSchema(document) {
97471
98005
  current = typeof current.extendsClassId === "string" ? classesById.get(current.extendsClassId) : void 0;
97472
98006
  }
97473
98007
  }
97474
- const rootMemberIds2 = /* @__PURE__ */ new Set();
98008
+ const rootMemberIds = /* @__PURE__ */ new Set();
97475
98009
  for (const member of document.members) {
97476
98010
  if (isMemberClass(member) && rootKindByClassId.has(member.classId)) {
97477
- rootMemberIds2.add(member.id);
98011
+ rootMemberIds.add(member.id);
97478
98012
  }
97479
98013
  }
97480
98014
  const segmentClassIds = /* @__PURE__ */ new Set();
@@ -97483,7 +98017,7 @@ function commitPreparationAnimationSchema(document) {
97483
98017
  segmentClassIds.add(classId);
97484
98018
  }
97485
98019
  }
97486
- return { rootMemberIds: rootMemberIds2, segmentClassIds };
98020
+ return { rootMemberIds, segmentClassIds };
97487
98021
  }
97488
98022
  function isAnimationSegmentValueRow(data, schema) {
97489
98023
  if (data === null || typeof data !== "object") return false;
@@ -104875,7 +105409,8 @@ function neoScriptCompilationProjectContract(document) {
104875
105409
  id: document.project.id,
104876
105410
  rootAssetsMemberId: document.project.rootAssetsMemberId,
104877
105411
  rootSaveFileMemberId: document.project.rootSaveFileMemberId,
104878
- rootSessionMemberId: document.project.rootSessionMemberId
105412
+ rootSessionMemberId: document.project.rootSessionMemberId,
105413
+ rootUserMemberId: document.project.rootUserMemberId
104879
105414
  },
104880
105415
  projectFiles: (document.projectFiles ?? []).map((file) => ({
104881
105416
  id: file.id,
@@ -107005,9 +107540,7 @@ function* prepareServerOwnedSchemaCommitPasses(args) {
107005
107540
  const accessModifierErrors = validateDocumentAccessModifiers({
107006
107541
  members: postDocument.members,
107007
107542
  classes: postDocument.classes,
107008
- rootMemberIds: new Set(
107009
- PROJECT_ROOTS.map((root) => postDocument.project[root.projectField])
107010
- ),
107543
+ rootMemberIds: new Set(projectRootMemberIds(postDocument.project)),
107011
107544
  dialogues: postDocument.dialogues,
107012
107545
  dialogueGroups: postDocument.dialogueGroups
107013
107546
  });
@@ -107297,11 +107830,9 @@ function attachPreparedValuePlacements(prepared, document) {
107297
107830
  }
107298
107831
  }
107299
107832
  const placements = projectValueHeadPlacementGraph({
107300
- rootMembers: [
107301
- { id: document.project.rootAssetsMemberId, label: "Assets" },
107302
- { id: document.project.rootSaveFileMemberId, label: "Save" },
107303
- { id: document.project.rootSessionMemberId, label: "Session" }
107304
- ],
107833
+ rootMembers: projectRootMembersInDisplayOrder(document.project).map(
107834
+ ({ root, memberId }) => ({ id: memberId, label: root.label })
107835
+ ),
107305
107836
  variantRoots: (document.variants ?? []).map((variant2) => ({
107306
107837
  variantId: variant2.id,
107307
107838
  valueId: variant2.valueId
@@ -110653,6 +111184,7 @@ var init_project_version_schema_commit = __esm({
110653
111184
  init_stored_value_shape();
110654
111185
  init_project2();
110655
111186
  init_internal_record_relations();
111187
+ init_project_root_members();
110656
111188
  init_world_content_sidecar_validation();
110657
111189
  init_variants();
110658
111190
  init_effective_value_reader();
@@ -111127,6 +111659,129 @@ var init_project_member_change_listener_validation = __esm({
111127
111659
  }
111128
111660
  });
111129
111661
 
111662
+ // ../src/database/user-delegate-target-validation.ts
111663
+ function assertStagedUserDelegateTargetsValid(projected, changes) {
111664
+ if (!changes.some((change) => TRIGGER_RECORD_KINDS.has(change.recordKind))) {
111665
+ return;
111666
+ }
111667
+ const userRootId = projected.project.rootUserMemberId;
111668
+ const changedRowIds = changes.some((change) => change.recordKind !== "value") ? null : new Set(changes.map((change) => change.recordId));
111669
+ const sources2 = authoredDelegateSources(projected, changedRowIds);
111670
+ if (sources2.length === 0) return;
111671
+ const resolver = new EffectiveStorageResolver({
111672
+ members: projected.members,
111673
+ classes: projected.classes,
111674
+ rootStorage: buildRootStorageMap(projected.project)
111675
+ });
111676
+ const userClasses = resolver.userClassIds();
111677
+ const schemaIndex = projectSchemaIndexFor(projected);
111678
+ const isUserMember = (memberId) => {
111679
+ const declaring = schemaIndex.declaringClassIds(memberId);
111680
+ if (declaring.size === 0) return false;
111681
+ for (const classId of declaring) {
111682
+ if (!userClasses.has(classId)) return false;
111683
+ }
111684
+ return true;
111685
+ };
111686
+ const durableValueIds = /* @__PURE__ */ new Set();
111687
+ for (const source of sources2) {
111688
+ const storages = resolver.effectiveStorages(source.ownerMemberId);
111689
+ if (storages.has(5 /* User */)) {
111690
+ for (const target of source.targets) {
111691
+ if (isUserMember(target.memberId)) continue;
111692
+ const member = resolver.member(target.memberId);
111693
+ const label = member === void 0 ? target.memberId : resolver.placementLabel(member);
111694
+ throw new Error(
111695
+ `Delegates in User data must target User members. \`${label}\` isn't one.`
111696
+ );
111697
+ }
111698
+ }
111699
+ if (!storages.has(2 /* Save */) && !storages.has(1 /* Immutable */)) {
111700
+ continue;
111701
+ }
111702
+ for (const target of source.targets) {
111703
+ if (target.valueId !== null) durableValueIds.add(target.valueId);
111704
+ }
111705
+ }
111706
+ if (durableValueIds.size === 0) return;
111707
+ const rootOwners = /* @__PURE__ */ new Map();
111708
+ resolveSemanticOwnerMembersForValues(
111709
+ projected,
111710
+ durableValueIds,
111711
+ void 0,
111712
+ rootOwners
111713
+ );
111714
+ for (const valueId of durableValueIds) {
111715
+ if (memberIdOf2(rootOwners.get(valueId)) !== userRootId) continue;
111716
+ throw new Error(
111717
+ `Save data can't hold a reference to \`root.User\` data. Row "${valueId}" is under \`root.User\`.`
111718
+ );
111719
+ }
111720
+ }
111721
+ function authoredDelegateSources(projected, changedRowIds) {
111722
+ const sources2 = [];
111723
+ for (const member of changedRowIds === null ? projected.members : []) {
111724
+ if (member.kind !== 26 /* NSAction */ && member.kind !== 25 /* NSDelegate */) {
111725
+ continue;
111726
+ }
111727
+ const defaultValue = member.defaultValue;
111728
+ if (defaultValue == null || !isLiteralValueContent(defaultValue)) continue;
111729
+ const targets = delegateTargets(defaultValue.value);
111730
+ if (targets.length > 0) {
111731
+ sources2.push({ ownerMemberId: member.id, targets });
111732
+ }
111733
+ }
111734
+ const rowTargets = /* @__PURE__ */ new Map();
111735
+ for (const row of projected.values) {
111736
+ if (changedRowIds !== null && !changedRowIds.has(row.id)) continue;
111737
+ const targets = delegateTargets(row.value);
111738
+ if (targets.length > 0) rowTargets.set(row.id, targets);
111739
+ }
111740
+ if (rowTargets.size === 0) return sources2;
111741
+ const owners = resolveSemanticOwnerMembersForValues(
111742
+ projected,
111743
+ new Set(rowTargets.keys())
111744
+ );
111745
+ for (const [rowId, owner] of owners) {
111746
+ if (owner.kind !== 26 /* NSAction */ && owner.kind !== 25 /* NSDelegate */) {
111747
+ continue;
111748
+ }
111749
+ const ownerMemberId = memberIdOf2(owner);
111750
+ const targets = rowTargets.get(rowId);
111751
+ if (ownerMemberId === null || targets === void 0) continue;
111752
+ sources2.push({ ownerMemberId, targets });
111753
+ }
111754
+ return sources2;
111755
+ }
111756
+ function delegateTargets(body) {
111757
+ if (isMemberDelegateTarget(body)) return [body];
111758
+ if (!isPlainRecord3(body) || !Array.isArray(body.listeners)) return [];
111759
+ return body.listeners.filter(isMemberDelegateTarget);
111760
+ }
111761
+ function memberIdOf2(member) {
111762
+ if (member === void 0) return null;
111763
+ const id2 = Reflect.get(member, "id");
111764
+ return typeof id2 === "string" ? id2 : null;
111765
+ }
111766
+ var TRIGGER_RECORD_KINDS;
111767
+ var init_user_delegate_target_validation = __esm({
111768
+ "../src/database/user-delegate-target-validation.ts"() {
111769
+ "use strict";
111770
+ init_member_kinds();
111771
+ init_member_storage();
111772
+ init_effective_storage();
111773
+ init_project_root_members();
111774
+ init_project_schema_index();
111775
+ init_constructor_argument_ownership();
111776
+ init_project_version_relation_sweep_trigger();
111777
+ TRIGGER_RECORD_KINDS = /* @__PURE__ */ new Set([
111778
+ "member",
111779
+ "class",
111780
+ "value"
111781
+ ]);
111782
+ }
111783
+ });
111784
+
111130
111785
  // ../src/models/exports/unity-static-members.ts
111131
111786
  function requiresUnitySingleton(member) {
111132
111787
  return isStaticMember(member) === true && !nativePatternMembers.has(member.id);
@@ -111179,6 +111834,7 @@ function animationTrackStorage(memberIdsRootToLeaf, membersById, childrenPrefix
111179
111834
  if (memberId === void 0) continue;
111180
111835
  const storage = declaredMemberStorageFromIndex(memberId, membersById);
111181
111836
  if (storage === 0 /* Inherit */) continue;
111837
+ if (storage === 5 /* User */) return null;
111182
111838
  return storage === 1 /* Immutable */ ? null : storage;
111183
111839
  }
111184
111840
  return null;
@@ -113171,36 +113827,36 @@ function readInterface(value) {
113171
113827
  `Interface "${value.id}" repeats member id "${memberValue.id}".`
113172
113828
  );
113173
113829
  }
113174
- const memberLabel2 = `Interface "${value.id}" member "${memberKey}"`;
113830
+ const memberLabel3 = `Interface "${value.id}" member "${memberKey}"`;
113175
113831
  assertNoRetiredFields(
113176
113832
  memberValue,
113177
113833
  ["accessModifierKind", "settable", "deferred"],
113178
- memberLabel2
113834
+ memberLabel3
113179
113835
  );
113180
113836
  assertOptionalOrdinal(
113181
113837
  memberValue.kind,
113182
113838
  INTERFACE_MEMBER_KINDS,
113183
- `${memberLabel2} kind`
113839
+ `${memberLabel3} kind`
113184
113840
  );
113185
113841
  if (memberValue.access != null && !isMemberAccessKind(memberValue.access)) {
113186
- throw new Error(`${memberLabel2} access is invalid.`);
113842
+ throw new Error(`${memberLabel3} access is invalid.`);
113187
113843
  }
113188
113844
  assertOptionalOrdinal(
113189
113845
  memberValue.accessors,
113190
113846
  PROPERTY_ACCESSOR_KINDS,
113191
- `${memberLabel2} accessors`
113847
+ `${memberLabel3} accessors`
113192
113848
  );
113193
113849
  assertOptionalOrdinal(
113194
113850
  memberValue.dispatch,
113195
113851
  FUNCTION_DISPATCH_KINDS,
113196
- `${memberLabel2} dispatch`
113852
+ `${memberLabel3} dispatch`
113197
113853
  );
113198
113854
  const isProperty = memberValue.kind == null || memberValue.kind === 0 /* Property */;
113199
113855
  if (isProperty && memberValue.dispatch != null) {
113200
- throw new Error(`${memberLabel2} property cannot declare dispatch.`);
113856
+ throw new Error(`${memberLabel3} property cannot declare dispatch.`);
113201
113857
  }
113202
113858
  if (!isProperty && memberValue.accessors != null) {
113203
- throw new Error(`${memberLabel2} function cannot declare accessors.`);
113859
+ throw new Error(`${memberLabel3} function cannot declare accessors.`);
113204
113860
  }
113205
113861
  memberIds.add(memberValue.id);
113206
113862
  }
@@ -115918,6 +116574,10 @@ function createGenerationContext(exportData, diagnostics, docs = EMPTY_UNITY_COD
115918
116574
  `${exportData.project.name}Neo`,
115919
116575
  "NeoProject"
115920
116576
  );
116577
+ const userClient = {
116578
+ className: toCSharpTypeName(`${exportData.project.name}UserNeo`, "NeoUser"),
116579
+ rootMemberId: exportData.project.rootUserMemberId
116580
+ };
115921
116581
  const enums = Object.values(exportData.enums);
115922
116582
  const enumNames = buildEnumNameMap(enums, diagnostics);
115923
116583
  const typeNames = buildNameMap(
@@ -115947,6 +116607,7 @@ function createGenerationContext(exportData, diagnostics, docs = EMPTY_UNITY_COD
115947
116607
  enumNames,
115948
116608
  [
115949
116609
  projectClassName,
116610
+ userClient.className,
115950
116611
  ...enumNames.values(),
115951
116612
  ...typeNames.values(),
115952
116613
  ...interfaceNames.values(),
@@ -115970,6 +116631,7 @@ function createGenerationContext(exportData, diagnostics, docs = EMPTY_UNITY_COD
115970
116631
  ),
115971
116632
  namespace: resolveGeneratedNamespace(exportData, diagnostics),
115972
116633
  projectClassName,
116634
+ userClient,
115973
116635
  singleton: resolveUnitySingleton(exportData),
115974
116636
  storageResolver: new EffectiveStorageResolver({
115975
116637
  members: Object.values(exportData.members),
@@ -116594,7 +117256,13 @@ function memberSavedForStorage(ctx, memberId, contextSaved, declarationClassId)
116594
117256
  return contextSaved;
116595
117257
  }
116596
117258
  function classAllowedStorage(ctx, classId) {
116597
- return resolveAllowedStorage(classId, ctx.classes);
117259
+ return ctx.storageResolver.allowedStorage(classId);
117260
+ }
117261
+ function generatedClientFor(ctx, classId) {
117262
+ if (classAllowedStorage(ctx, classId) === 5 /* User */) {
117263
+ return { typeName: ctx.userClient.className, parameter: "user" };
117264
+ }
117265
+ return { typeName: ctx.projectClassName, parameter: "project" };
116598
117266
  }
116599
117267
  function isWritableConstraint(storage) {
116600
117268
  return storage === 2 /* Save */ || storage === 3 /* Session */ || storage === 4 /* Writable */;
@@ -117217,6 +117885,7 @@ function nsGetterClassReturnSaved(ctx, member) {
117217
117885
  const ownership = nsGetterReturnOwnership(ctx, getter, visited);
117218
117886
  if (ownership === "save") return true;
117219
117887
  if (ownership === "session") return true;
117888
+ if (ownership === "user") return true;
117220
117889
  if (ownership === "asset") return false;
117221
117890
  return null;
117222
117891
  }
@@ -117502,9 +118171,8 @@ function pointerSource2(ctx, pointer, variables, visitedGetterMemberIds) {
117502
118171
  );
117503
118172
  if (pointer.keyOf.pointer.type === "variable" /* variable */ && pointer.keyOf.pointer.variableId === "__root__") {
117504
118173
  const key2 = stringLiteralPointerValue(pointer.keyOf.key);
117505
- if (key2 === "Save") return sourceForRootMember(ctx, "save");
117506
- if (key2 === "Session") return sourceForRootMember(ctx, "session");
117507
- if (key2 === "Assets") return sourceForRootMember(ctx, "asset");
118174
+ const root = PROJECT_ROOTS.find((candidate) => candidate.key === key2);
118175
+ if (root !== void 0) return sourceForRootMember(ctx, root);
117508
118176
  }
117509
118177
  const key = stringLiteralPointerValue(pointer.keyOf.key);
117510
118178
  return key === null ? unknownSource() : sourceForMemberAccess(ctx, parent, key);
@@ -117712,12 +118380,12 @@ function sourceForCallGetter(ctx, memberId, visitedGetterMemberIds) {
117712
118380
  };
117713
118381
  }
117714
118382
  function sourceForRootMember(ctx, root) {
117715
- const memberId = root === "save" ? ctx.exportData.project.rootSaveFileMemberId : root === "session" ? ctx.exportData.project.rootSessionMemberId : ctx.exportData.project.rootAssetsMemberId;
118383
+ const memberId = ctx.exportData.project[root.projectField];
117716
118384
  const member = resolveMemberById(ctx, memberId, "project.rootMemberId");
117717
118385
  return {
117718
118386
  memberId,
117719
118387
  typeInfo: member === null ? null : memberTypeInfoForCodegen(ctx, member),
117720
- ownership: root
118388
+ ownership: pointerOwnershipForStorage(root.storage)
117721
118389
  };
117722
118390
  }
117723
118391
  function sourceForMemberAccess(ctx, parent, key) {
@@ -117771,6 +118439,7 @@ function ownershipForMemberStorage(ctx, memberId) {
117771
118439
  const storages = ctx.storageResolver.effectiveStorages(memberId);
117772
118440
  if (storages.has(2 /* Save */)) return "save";
117773
118441
  if (storages.has(3 /* Session */)) return "session";
118442
+ if (storages.has(5 /* User */)) return "user";
117774
118443
  if (storages.has(1 /* Immutable */)) return "asset";
117775
118444
  return null;
117776
118445
  }
@@ -117813,6 +118482,8 @@ function storageForPointerOwnership(ownership) {
117813
118482
  return 2 /* Save */;
117814
118483
  case "session":
117815
118484
  return 3 /* Session */;
118485
+ case "user":
118486
+ return 5 /* User */;
117816
118487
  case "asset":
117817
118488
  return 1 /* Immutable */;
117818
118489
  }
@@ -117823,6 +118494,8 @@ function pointerOwnershipForStorage(storage) {
117823
118494
  return "save";
117824
118495
  case 3 /* Session */:
117825
118496
  return "session";
118497
+ case 5 /* User */:
118498
+ return "user";
117826
118499
  case 1 /* Immutable */:
117827
118500
  return "asset";
117828
118501
  }
@@ -118190,9 +118863,7 @@ function collectProjectDocumentInheritanceFlagsErrors(document, phase = "complet
118190
118863
  phase,
118191
118864
  members: document.members,
118192
118865
  classes: document.classes,
118193
- rootMemberIds: new Set(
118194
- PROJECT_ROOTS.map((root) => document.project[root.projectField])
118195
- ),
118866
+ rootMemberIds: new Set(projectRootMemberIds(document.project)),
118196
118867
  signatureProjector: {
118197
118868
  format: formatGeneratedInstanceMemberSignature,
118198
118869
  resolveLocal: (classId, schemaKey) => getGeneratedSignatures().projectLocal(classId, schemaKey),
@@ -118994,12 +119665,14 @@ function assertProjectVersionWholeGraphWritesValid(args) {
118994
119665
  assertWorldFootprintEntryTypes(projected, changes);
118995
119666
  assertStagedSchemaIdentifiersValid(projected, changes);
118996
119667
  assertStagedActionListenersValid(projected, changes);
119668
+ assertStagedUserDelegateTargetsValid(projected, changes);
118997
119669
  assertAuthoredMemberChangeListenersValid(args.document, projected, changes);
118998
119670
  assertStagedVariantMemberValuesValid(projected, changes);
118999
119671
  assertStagedUnitySingletonValid(projected, changes);
119000
119672
  assertStagedProjectInterfaceValid(projected, changes);
119001
119673
  if (changesRequireInheritanceFlagsValidation(changes)) {
119002
119674
  assertProjectDocumentInheritanceFlagsValid(projected);
119675
+ assertProjectDocumentStorageValid(projected);
119003
119676
  }
119004
119677
  assertStagedTileGridReferencesValid(args.document, projected, changes);
119005
119678
  assertStagedInternalRecordRelationsValid(args.document, projected, changes);
@@ -119018,6 +119691,14 @@ function assertProjectVersionWholeGraphWritesValid(args) {
119018
119691
  });
119019
119692
  }
119020
119693
  }
119694
+ function assertProjectDocumentStorageValid(projected) {
119695
+ const errors = validateDocumentStorage({
119696
+ members: projected.members,
119697
+ classes: projected.classes,
119698
+ rootStorage: buildRootStorageMap(projected.project)
119699
+ });
119700
+ if (errors.length > 0) throw new Error(errors[0].message);
119701
+ }
119021
119702
  function assertStagedUnorderedContainmentValid(current, projected, changes) {
119022
119703
  const currentValues = new Map(
119023
119704
  current.values.map((value) => [value.id, value])
@@ -119597,11 +120278,9 @@ function assertStagedSchemaIdentifiersValid(projected, changes) {
119597
120278
  (change) => change.operation !== "delete" && ["class", "enum", "interface", "member"].includes(change.recordKind)
119598
120279
  );
119599
120280
  if (relevant.length === 0) return;
119600
- const directlyAuthoredMemberIds = /* @__PURE__ */ new Set([
119601
- projected.project.rootAssetsMemberId,
119602
- projected.project.rootSaveFileMemberId,
119603
- projected.project.rootSessionMemberId
119604
- ]);
120281
+ const directlyAuthoredMemberIds = new Set(
120282
+ projectRootMemberIds(projected.project)
120283
+ );
119605
120284
  for (const schemaClass2 of projected.classes) {
119606
120285
  for (const memberId of Object.values(schemaClass2.schema ?? {})) {
119607
120286
  directlyAuthoredMemberIds.add(memberId);
@@ -120169,7 +120848,9 @@ var KNOWN_TRANSACTION_OPERATIONS, INTERFACE_VALIDATED_RECORD_KINDS, ACTION_LISTE
120169
120848
  var init_project_version_whole_graph_validation = __esm({
120170
120849
  "../src/database/project-version-whole-graph-validation.ts"() {
120171
120850
  "use strict";
120851
+ init_project_root_members();
120172
120852
  init_project_member_change_listener_validation();
120853
+ init_user_delegate_target_validation();
120173
120854
  init_project_world_placement_bindings();
120174
120855
  init_unity_static_members();
120175
120856
  init_project_version_types();
@@ -120181,6 +120862,7 @@ var init_project_version_whole_graph_validation = __esm({
120181
120862
  init_projectInterfaceValidation();
120182
120863
  init_project_document_inheritance_validation();
120183
120864
  init_project_root_members();
120865
+ init_effective_storage();
120184
120866
  init_project_schema_identifier_validation();
120185
120867
  init_packed_value_write_fold();
120186
120868
  init_project_version_write_changes();
@@ -120514,11 +121196,9 @@ function refreshReferencedOutsideDocument(args) {
120514
121196
  }
120515
121197
  function inMemoryScopeReader(document, records2) {
120516
121198
  const canonicalPlacementByValueId = projectValueHeadPlacementGraph({
120517
- rootMembers: [
120518
- { id: document.project.rootAssetsMemberId, label: "Assets" },
120519
- { id: document.project.rootSaveFileMemberId, label: "Save" },
120520
- { id: document.project.rootSessionMemberId, label: "Session" }
120521
- ],
121199
+ rootMembers: projectRootMembersInDisplayOrder(document.project).map(
121200
+ ({ root, memberId }) => ({ id: memberId, label: root.label })
121201
+ ),
120522
121202
  variantRoots: (document.variants ?? []).map((variant2) => ({
120523
121203
  variantId: variant2.id,
120524
121204
  valueId: variant2.valueId
@@ -120706,6 +121386,7 @@ var init_client_commit_preparation = __esm({
120706
121386
  init_valueHeadPlacementGraph();
120707
121387
  init_build_default_member_value();
120708
121388
  init_packed_value_encoding();
121389
+ init_project_root_members();
120709
121390
  init_commit_preparation_animation_scope();
120710
121391
  init_commit_preparation_scope_planner();
120711
121392
  init_packed_value_write_fold();
@@ -120827,12 +121508,10 @@ function sourceDiagnosticMapper(analysis) {
120827
121508
  };
120828
121509
  }
120829
121510
  function collectSourceSchemaErrors(document) {
120830
- const rootMemberIds2 = new Set(
120831
- PROJECT_ROOTS.map((root) => document.project[root.projectField])
120832
- );
121511
+ const rootMemberIds = new Set(projectRootMemberIds(document.project));
120833
121512
  const errors = [];
120834
121513
  for (const validate3 of [
120835
- () => validateDocumentAccessModifiers({ ...document, rootMemberIds: rootMemberIds2 }),
121514
+ () => validateDocumentAccessModifiers({ ...document, rootMemberIds }),
120836
121515
  () => collectProjectDocumentInheritanceFlagsErrors(document, "schema")
120837
121516
  ]) {
120838
121517
  try {
@@ -121113,8 +121792,10 @@ function createListenerLoweringDocument(args, active) {
121113
121792
  compiledConstructors.map((constructor2) => [constructor2.id, constructor2])
121114
121793
  );
121115
121794
  const rootByName = /* @__PURE__ */ new Map();
121116
- for (const root of PROJECT_ROOTS) {
121117
- const id2 = members.get(document.project[root.projectField])?.valueId;
121795
+ for (const { root, memberId } of projectRootMembersInDisplayOrder(
121796
+ document.project
121797
+ )) {
121798
+ const id2 = members.get(memberId)?.valueId;
121118
121799
  if (typeof id2 === "string") rootByName.set(root.key, id2);
121119
121800
  }
121120
121801
  return {
@@ -123678,29 +124359,32 @@ function lowerProjectSchemaV4(base, analysis, options = {}) {
123678
124359
  };
123679
124360
  }
123680
124361
  function lowerProtectedRootMembers(base, analysis) {
123681
- const storageNames = ["immutable", "save", "session"];
123682
- const candidates = storageNames.map(
123683
- (storage) => base.members.filter(
123684
- (member) => member.owner.kind === "loose" && member.storage === storage
124362
+ const candidates = PROJECT_ROOT_SOURCE_SLOTS.map(
124363
+ (slot) => base.members.filter(
124364
+ (member) => member.owner.kind === "loose" && member.storage === slot.storage.toLowerCase()
123685
124365
  )
123686
124366
  );
123687
124367
  if (candidates.every((entries) => entries.length === 0)) return [];
123688
- if (candidates.some((entries) => entries.length !== 1)) {
123689
- throw new Error(
123690
- "Pulled schema does not uniquely identify the immutable/save/session project root members."
123691
- );
123692
- }
124368
+ PROJECT_ROOT_SOURCE_SLOTS.forEach((slot, index) => {
124369
+ if (candidates[index].length !== 1) {
124370
+ throw new Error(
124371
+ `Pulled schema does not uniquely identify the ${slot.name} project root member.`
124372
+ );
124373
+ }
124374
+ });
123693
124375
  const globals = analysis.configuration.globals.filter(
123694
124376
  (global2) => global2.type.name === "Root"
123695
124377
  );
123696
- if (globals.length === 0) return candidates.map((entries) => entries[0]);
124378
+ if (globals.length === 0) {
124379
+ return candidates.map((entries) => entries[0]);
124380
+ }
123697
124381
  if (globals.length !== 1 || globals[0].name !== "root") {
123698
124382
  throw new Error("Format-4 source requires exactly one Root root global.");
123699
124383
  }
123700
124384
  const global = globals[0];
123701
124385
  const slots = parseProjectRootInitializer(global.initializer);
123702
124386
  if (slots.length !== PROJECT_ROOT_SOURCE_SLOTS.length) {
123703
- throw new Error("Root must contain exactly Assets, Save, and Session.");
124387
+ throw new Error(projectRootSlotCountError(slots));
123704
124388
  }
123705
124389
  const classes = new Map(base.classes.map((entry) => [entry.id, entry]));
123706
124390
  return PROJECT_ROOT_SOURCE_SLOTS.map((expected, index) => {
@@ -123763,7 +124447,7 @@ function collectInitDefaultConversions(changes, stateRecords) {
123763
124447
  if (!Object.prototype.hasOwnProperty.call(baseDefault, "value")) continue;
123764
124448
  candidates.push({
123765
124449
  memberId: change.recordId,
123766
- memberLabel: memberLabel(change.baseData, change.recordId),
124450
+ memberLabel: memberLabel2(change.baseData, change.recordId),
123767
124451
  initCode,
123768
124452
  baseDefaultValue: baseDefault.value
123769
124453
  });
@@ -123893,7 +124577,7 @@ function constructedClassName(initCode) {
123893
124577
  return null;
123894
124578
  }
123895
124579
  }
123896
- function memberLabel(baseData3, fallback) {
124580
+ function memberLabel2(baseData3, fallback) {
123897
124581
  const name = baseData3.name;
123898
124582
  return typeof name === "string" && name.length > 0 ? name : fallback;
123899
124583
  }
@@ -125796,7 +126480,8 @@ function indexValueSymbols(state, analysis) {
125796
126480
  const roots = [
125797
126481
  ["root.Assets", project.rootAssetsMemberId],
125798
126482
  ["root.Save", project.rootSaveFileMemberId],
125799
- ["root.Session", project.rootSessionMemberId]
126483
+ ["root.Session", project.rootSessionMemberId],
126484
+ ["root.User", project.rootUserMemberId]
125800
126485
  ];
125801
126486
  for (const [sourceName, memberId] of roots) {
125802
126487
  if (typeof memberId !== "string") continue;
@@ -126224,10 +126909,11 @@ function emitProjectRootSourceV4(records2, manifest, materializedConstructors, l
126224
126909
  );
126225
126910
  if (!project) return null;
126226
126911
  const projectData = requiredObject(project.data, "project");
126227
- const memberIds = rootMemberIds(projectData);
126228
126912
  const members = new Map(
126229
126913
  manifest.members.map((member) => [member.id, member])
126230
126914
  );
126915
+ const rootSlots = projectRootSlots(projectData);
126916
+ const memberIds = rootSlots.map((root) => root.memberId);
126231
126917
  const classes = new Map(manifest.classes.map((entry) => [entry.id, entry]));
126232
126918
  const values = emitStoredValueBindingSourcesV4(records2, memberIds, {
126233
126919
  ...options,
@@ -126240,8 +126926,7 @@ function emitProjectRootSourceV4(records2, manifest, materializedConstructors, l
126240
126926
  materializedConstructors.set(valueId, expression);
126241
126927
  }
126242
126928
  }
126243
- const declarations = PROJECT_ROOT_SOURCE_SLOTS.map((slot, index) => {
126244
- const memberId = memberIds[index];
126929
+ const declarations = rootSlots.map(({ slot, memberId }) => {
126245
126930
  const member = members.get(memberId);
126246
126931
  if (!member)
126247
126932
  throw new Error(`Project root member ${memberId} was not pulled.`);
@@ -126294,13 +126979,13 @@ function lowerProjectRootSourceV4(state, analysis, manifest, options = {}) {
126294
126979
  if (validated === null) {
126295
126980
  return { records: [], memberValueIds: /* @__PURE__ */ new Map(), seeds: /* @__PURE__ */ new Map() };
126296
126981
  }
126297
- const { expectedIds, global, slots } = validated;
126298
- const bindings = PROJECT_ROOT_SOURCE_SLOTS.map((expected, index) => ({
126299
- memberId: expectedIds[index],
126982
+ const { expected, global, slots } = validated;
126983
+ const bindings = expected.map((root, index) => ({
126984
+ memberId: root.memberId,
126300
126985
  purpose: "stored",
126301
126986
  initializer: slots[index].initializer,
126302
126987
  source: global.source,
126303
- label: `root.${expected.name}`
126988
+ label: `root.${root.slot.name}`
126304
126989
  }));
126305
126990
  return lowerStoredValueBindingsV4(state, manifest, bindings, {
126306
126991
  analysis,
@@ -126313,7 +126998,7 @@ function validateProjectRootEnvelopeV4(state, analysis, parsedInitializers) {
126313
126998
  );
126314
126999
  if (!project) return null;
126315
127000
  const projectData = requiredObject(project.data, "project");
126316
- const expectedIds = rootMemberIds(projectData);
127001
+ const expected = projectRootSlots(projectData);
126317
127002
  const roots = analysis.configuration.globals.filter(
126318
127003
  (global2) => global2.type.name === "Root"
126319
127004
  );
@@ -126325,15 +127010,14 @@ function validateProjectRootEnvelopeV4(state, analysis, parsedInitializers) {
126325
127010
  throw new Error("The protected project root global must be named root.");
126326
127011
  }
126327
127012
  const slots = parseProjectRootInitializer(global.initializer);
126328
- if (slots.length !== PROJECT_ROOT_SOURCE_SLOTS.length) {
126329
- throw new Error("Root must contain exactly Assets, Save, and Session.");
127013
+ if (slots.length !== expected.length) {
127014
+ throw new Error(projectRootSlotCountError(slots));
126330
127015
  }
126331
- PROJECT_ROOT_SOURCE_SLOTS.forEach((expected, index) => {
127016
+ expected.forEach(({ slot: rootSlot, memberId }, index) => {
126332
127017
  const slot = slots[index];
126333
- const memberId = expectedIds[index];
126334
- if (slot.id !== memberId || slot.name !== expected.name || slot.type !== expected.type || slot.storage !== expected.storage) {
127018
+ if (slot.id !== memberId || slot.name !== rootSlot.name || slot.type !== rootSlot.type || slot.storage !== rootSlot.storage) {
126335
127019
  throw new Error(
126336
- `Root slot ${expected.name} is protected and must retain member id ${memberId}, type/name ${expected.type} ${expected.name}, and @storage(allowed: .${expected.storage}).`
127020
+ `Root slot ${rootSlot.name} is protected and must retain member id ${memberId}, type/name ${rootSlot.type} ${rootSlot.name}, and @storage(allowed: .${rootSlot.storage}).`
126337
127021
  );
126338
127022
  }
126339
127023
  const memberData = state[`member:${memberId}`]?.data;
@@ -126351,19 +127035,20 @@ function validateProjectRootEnvelopeV4(state, analysis, parsedInitializers) {
126351
127035
  if (authoredValueId === void 0) return;
126352
127036
  if (authoredValueId.kind !== "litString" || authoredValueId.value !== memberData.valueId) {
126353
127037
  throw new Error(
126354
- `Root value root.${expected.name} must retain stable value id ${memberData.valueId}.`
127038
+ `Root value root.${rootSlot.name} must retain stable value id ${memberData.valueId}.`
126355
127039
  );
126356
127040
  }
126357
127041
  });
126358
- return { expectedIds, global, slots };
127042
+ return { expected, global, slots };
126359
127043
  }
126360
- function rootMemberIds(project) {
126361
- return ROOT_PROJECT_FIELDS.map((field) => {
127044
+ function projectRootSlots(project) {
127045
+ return PROJECT_ROOT_SOURCE_SLOTS.map((slot) => {
127046
+ const field = ROOT_PROJECT_FIELDS[slot.name];
126362
127047
  const id2 = project[field];
126363
127048
  if (typeof id2 !== "string" || id2.length === 0) {
126364
127049
  throw new Error(`Project record has no ${field}.`);
126365
127050
  }
126366
- return id2;
127051
+ return { slot, memberId: id2 };
126367
127052
  });
126368
127053
  }
126369
127054
  function assertRootMember(member, name, storage) {
@@ -126501,14 +127186,12 @@ function rootSlotValueIds(state) {
126501
127186
  (record7) => record7.recordKind === "project"
126502
127187
  );
126503
127188
  if (!isObjectRecord2(project?.data)) return null;
126504
- const memberIds = rootMemberIds(project.data);
126505
127189
  const values = /* @__PURE__ */ new Map();
126506
- PROJECT_ROOT_SOURCE_SLOTS.forEach((slot, index) => {
126507
- const memberId = memberIds[index];
126508
- const data = memberId === void 0 ? void 0 : state[`member:${memberId}`]?.data;
127190
+ for (const { slot, memberId } of projectRootSlots(project.data)) {
127191
+ const data = state[`member:${memberId}`]?.data;
126509
127192
  if (isObjectRecord2(data) && typeof data.valueId === "string")
126510
127193
  values.set(slot.name, data.valueId);
126511
- });
127194
+ }
126512
127195
  return values;
126513
127196
  }
126514
127197
  function walkRootValuePath(path, state, registry, placements, rootValues, fail2, resolveVirtualPath) {
@@ -126516,7 +127199,7 @@ function walkRootValuePath(path, state, registry, placements, rootValues, fail2,
126516
127199
  const slotName = segments[1];
126517
127200
  if (segments[0] !== "root" || slotName === void 0) {
126518
127201
  fail2(
126519
- `collectionValue path ${path} must start at a root slot (root.Assets, root.Save, or root.Session).`
127202
+ `collectionValue path ${path} must start at a root slot (root.Assets, root.Save, root.Session, or root.User).`
126520
127203
  );
126521
127204
  return null;
126522
127205
  }
@@ -126525,7 +127208,7 @@ function walkRootValuePath(path, state, registry, placements, rootValues, fail2,
126525
127208
  );
126526
127209
  if (slotIndex < 0 || rootValues === null) {
126527
127210
  fail2(
126528
- `collectionValue path ${path} names root slot ${slotName ?? "<none>"}, which is not Assets, Save, or Session.`
127211
+ `collectionValue path ${path} names root slot ${slotName ?? "<none>"}, which is not Assets, Save, Session, or User.`
126529
127212
  );
126530
127213
  return null;
126531
127214
  }
@@ -126637,11 +127320,12 @@ var init_root_source = __esm({
126637
127320
  init_value_sources();
126638
127321
  init_source_format();
126639
127322
  init_stored_value_placements();
126640
- ROOT_PROJECT_FIELDS = [
126641
- "rootAssetsMemberId",
126642
- "rootSaveFileMemberId",
126643
- "rootSessionMemberId"
126644
- ];
127323
+ ROOT_PROJECT_FIELDS = {
127324
+ Assets: "rootAssetsMemberId",
127325
+ Save: "rootSaveFileMemberId",
127326
+ Session: "rootSessionMemberId",
127327
+ User: "rootUserMemberId"
127328
+ };
126645
127329
  ROOT_PATH_PENDING_PREFIX = "__pending__:root-path:";
126646
127330
  }
126647
127331
  });
@@ -126724,7 +127408,8 @@ var init_root_value_paths = __esm({
126724
127408
  ROOT_PROJECT_FIELDS2 = [
126725
127409
  ["rootAssetsMemberId", "Assets"],
126726
127410
  ["rootSaveFileMemberId", "Save"],
126727
- ["rootSessionMemberId", "Session"]
127411
+ ["rootSessionMemberId", "Session"],
127412
+ ["rootUserMemberId", "User"]
126728
127413
  ];
126729
127414
  }
126730
127415
  });
@@ -131639,7 +132324,7 @@ function buildValueEmitIndexesV4(records2, manifest) {
131639
132324
  bindings.set(valueId, candidates);
131640
132325
  };
131641
132326
  const project = readDocument().project;
131642
- for (const root of PROJECT_ROOTS) add(project[root.projectField]);
132327
+ for (const memberId of projectRootMemberIds(project)) add(memberId);
131643
132328
  for (const [id2, member] of members)
131644
132329
  if (member.modifier === 3 /* Static */) add(id2);
131645
132330
  let compilation;
@@ -131900,9 +132585,8 @@ function emitBindingListeners(context, member, rootId, wiring, source, defaultVa
131900
132585
  if (parts[0] === "root") {
131901
132586
  const root = PROJECT_ROOTS.find((root2) => root2.key === parts[1]);
131902
132587
  if (root === void 0) return false;
131903
- const rootMember = context.members.get(
131904
- context.readDocument().project[root.projectField]
131905
- );
132588
+ const rootMemberId = context.readDocument().project[root.projectField];
132589
+ const rootMember = context.members.get(rootMemberId);
131906
132590
  receiverClassId = typeof rootMember?.classId === "string" ? rootMember.classId : void 0;
131907
132591
  offset = 2;
131908
132592
  }
@@ -132144,7 +132828,7 @@ function emitVariantValueSourcesV4(records2, manifest, options = {}) {
132144
132828
  variant2.id,
132145
132829
  `new(
132146
132830
  ${authored.map(
132147
- ([name, expression]) => ` ${name}: ${indentNeoSourceNonEmptyLines(expression, 2).trimStart()},`
132831
+ ([name, expression]) => ` ${name}: ${indentNeoSource(expression, 2).trimStart()},`
132148
132832
  ).join("\n")}
132149
132833
  )`
132150
132834
  );
@@ -138386,7 +139070,7 @@ function classValueBody(context, member, value, visited, targetTyped, outerEnvir
138386
139070
  }
138387
139071
  if (partial) {
138388
139072
  return fields.length === 0 ? "new { }" : `new {
138389
- ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
139073
+ ${fields.map((field) => indentNeoSource(field, 2)).join(",\n")}
138390
139074
  }`;
138391
139075
  }
138392
139076
  const constructor2 = storedConstruction !== null ? storedConstruction : projection.arguments.length > 0 ? constructorCallSource(
@@ -138399,7 +139083,7 @@ ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
138399
139083
  if (typeof value.id === "string")
138400
139084
  context.materializedConstructorOverrideKeys.set(value.id, overrideKeys);
138401
139085
  return fields.length === 0 ? constructor2 : `${storedConstruction !== null || projection.arguments.length > 0 ? constructor2 : targetTyped ? "new()" : `new ${name}`} {
138402
- ${fields.map((field) => indentNeoSourceNonEmptyLines(field, 2)).join(",\n")}
139086
+ ${fields.map((field) => indentNeoSource(field, 2)).join(",\n")}
138403
139087
  }`;
138404
139088
  }
138405
139089
  function emitsAsMemberDefaultDerivedAbsence(context, member, valueId) {
@@ -138681,7 +139365,7 @@ function constructorCallSource(callee, argumentsValue) {
138681
139365
  return inline;
138682
139366
  }
138683
139367
  return `${callee}(
138684
- ${argumentsValue.map((argument2) => `${indentNeoSourceNonEmptyLines(argument2, 2)},`).join("\n")}
139368
+ ${argumentsValue.map((argument2) => `${indentNeoSource(argument2, 2)},`).join("\n")}
138685
139369
  )`;
138686
139370
  }
138687
139371
  function storedValueConstructor(context, schemaClass2, recordedConstructorId) {
@@ -139182,7 +139866,7 @@ function storedTypedRowSource(context, type, valueId, environment, visited, writ
139182
139866
  ) : [...context.values].filter(([, candidate]) => candidate.containerId === valueId).map(([id2]) => id2).sort(compareCodePoints);
139183
139867
  const expression = ids.length === 0 ? "[]" : `[
139184
139868
  ${ids.map(
139185
- (id2) => indentNeoSourceNonEmptyLines(
139869
+ (id2) => indentNeoSource(
139186
139870
  storedTypedRowSource(
139187
139871
  context,
139188
139872
  type.entryType,
@@ -139211,7 +139895,7 @@ ${ids.map(
139211
139895
  const expression = entries.length === 0 ? "{}" : `{
139212
139896
  ${entries.flatMap(
139213
139897
  ([key, id2]) => typeof id2 === "string" ? [
139214
- indentNeoSourceNonEmptyLines(
139898
+ indentNeoSource(
139215
139899
  `${emitDictionaryKey(key, keyOptions)}: ${storedTypedRowSource(
139216
139900
  context,
139217
139901
  type.entryType,
@@ -139619,7 +140303,7 @@ function listValue(context, member, value, visited, inheritedEnvironment) {
139619
140303
  if (ids.length === 0) return "[]";
139620
140304
  return `[
139621
140305
  ${ids.map(
139622
- (id2) => indentNeoSourceNonEmptyLines(
140306
+ (id2) => indentNeoSource(
139623
140307
  emitValue(context, entryMember, id2, {
139624
140308
  environment,
139625
140309
  definitionSite: context.symbolsByValueId.get(id2) === void 0,
@@ -139648,7 +140332,7 @@ function dictionaryValue(context, member, value, visited, inheritedEnvironment)
139648
140332
  return `{
139649
140333
  ${entries.flatMap(
139650
140334
  ([key, id2]) => typeof id2 === "string" ? [
139651
- indentNeoSourceNonEmptyLines(
140335
+ indentNeoSource(
139652
140336
  `${emitDictionaryKey(key, keyOptions)}: ${emitValue(
139653
140337
  context,
139654
140338
  entryMember,
@@ -139887,7 +140571,7 @@ function structuredLeafSource(context, kind, member, body) {
139887
140571
  });
139888
140572
  if (assignments.length === 0) return "new { }";
139889
140573
  return `new {
139890
- ${assignments.map((entry) => indentNeoSourceNonEmptyLines(entry, 2)).join(",\n")}
140574
+ ${assignments.map((entry) => indentNeoSource(entry, 2)).join(",\n")}
139891
140575
  }`;
139892
140576
  }
139893
140577
  if (kind === 11 /* Sprite */) {
@@ -149783,9 +150467,9 @@ var init_service = __esm({
149783
150467
  position
149784
150468
  );
149785
150469
  if (!resolved) return null;
149786
- const memberLabel2 = resolved.symbol.kind === "member" || resolved.symbol.kind === "enumOption" || resolved.symbol.kind === "graphChild";
150470
+ const memberLabel3 = resolved.symbol.kind === "member" || resolved.symbol.kind === "enumOption" || resolved.symbol.kind === "graphChild";
149787
150471
  const binding = resolved.symbol.kind === "local" || resolved.symbol.kind === "parameter" || resolved.symbol.kind === "genericParameter" || resolved.symbol.kind === "flowBinding";
149788
- let validNeoName = memberLabel2 ? isValidNeoProjectMemberIdentifier(newName) : binding ? isValidNeoProjectBindingIdentifier(newName) : isValidNeoProjectIdentifier(newName);
150472
+ let validNeoName = memberLabel3 ? isValidNeoProjectMemberIdentifier(newName) : binding ? isValidNeoProjectBindingIdentifier(newName) : isValidNeoProjectIdentifier(newName);
149789
150473
  if (resolved.symbol.kind === "local")
149790
150474
  validNeoName = isValidNeoScriptBindingIdentifier(newName);
149791
150475
  validName = validNeoName && isGeneratedCSharpIdentifier(newName) && !isGeneratedCSharpReservedKeyword(newName);
@@ -156098,12 +156782,8 @@ function getUnityReferencedProjectFileIds(args) {
156098
156782
  genericEnv: EMPTY_GENERIC_ENV
156099
156783
  });
156100
156784
  }
156101
- const rootMemberIds2 = [
156102
- args.project.rootAssetsMemberId,
156103
- args.project.rootSaveFileMemberId,
156104
- args.project.rootSessionMemberId
156105
- ];
156106
- for (const rootMemberId of rootMemberIds2) {
156785
+ const rootMemberIds = projectRootMemberIds(args.project);
156786
+ for (const rootMemberId of rootMemberIds) {
156107
156787
  collectUnityReferencedProjectFileIds({
156108
156788
  memberId: rootMemberId,
156109
156789
  fileIds,
@@ -156328,6 +157008,7 @@ var EMPTY_GENERIC_ENV, NO_FILES, NO_TEXTURE_TEMPLATES, NO_CONSTRUCTORS, NO_INTER
156328
157008
  var init_project_unity_export_utils = __esm({
156329
157009
  "../src/models/exports/project-unity-export-utils.ts"() {
156330
157010
  "use strict";
157011
+ init_project_root_members();
156331
157012
  init_neoscript_file_references();
156332
157013
  init_compiler();
156333
157014
  init_variant_value_graph();
@@ -156840,6 +157521,11 @@ function generateUnitySchemaTypes(exportData, docs = EMPTY_UNITY_CODEGEN_DOCS) {
156840
157521
  invokers
156841
157522
  );
156842
157523
  emit("project:client", ctx.projectClassName, client.core);
157524
+ const userClient = emit(
157525
+ "project:user-client",
157526
+ ctx.userClient.className,
157527
+ generateUserClient(ctx, ctx.userClient)
157528
+ );
156843
157529
  const clientPartial = (lines) => [
156844
157530
  `public sealed partial class ${ctx.projectClassName}`,
156845
157531
  "{",
@@ -156859,6 +157545,8 @@ function generateUnitySchemaTypes(exportData, docs = EMPTY_UNITY_CODEGEN_DOCS) {
156859
157545
  const body = [
156860
157546
  ...client.contract,
156861
157547
  "",
157548
+ ...userClient,
157549
+ "",
156862
157550
  ...generateDialogueClasses(ctx, emit),
156863
157551
  ...emit(
156864
157552
  "dialogues:memory",
@@ -157062,7 +157750,11 @@ function generateProjectClient(ctx, projectClassName, factories, invokers) {
157062
157750
  "var client = await new NeoLoader().Load(synchronizer, assetDatabase, localizationOptions, null, saveOptions, cancellationToken);",
157063
157751
  "try",
157064
157752
  "{",
157065
- ...indent([`return new ${projectClassName}(client, dialogueOptions);`]),
157753
+ ...indent([
157754
+ // P104 §4.1: the user client starts before the save client's runtime.
157755
+ `if (client.ReadsUserFile) ${ctx.userClient.className}.RequireInstance();`,
157756
+ `return new ${projectClassName}(client, dialogueOptions);`
157757
+ ]),
157066
157758
  "}",
157067
157759
  "catch",
157068
157760
  "{",
@@ -157098,6 +157790,47 @@ function generateProjectClient(ctx, projectClassName, factories, invokers) {
157098
157790
  )
157099
157791
  };
157100
157792
  }
157793
+ function generateUserClient(ctx, { className: userClassName, rootMemberId }) {
157794
+ const project = ctx.projectClassName;
157795
+ const userType = resolveRootSchemaClassInfo(
157796
+ ctx,
157797
+ rootMemberId,
157798
+ "project.rootUserMemberId"
157799
+ );
157800
+ const userApiType = userType === null ? "NeoMemberClassWritable" : schemaClassFamilyName(ctx, userType.classId, userType.typeName, true);
157801
+ return [
157802
+ `public sealed partial class ${userClassName} : NeoUserClient`,
157803
+ "{",
157804
+ ...indent([
157805
+ "/// <summary>The user client. Throws until NeoProjectStore.Current has loaded the user file.</summary>",
157806
+ `public static ${userClassName} Instance => RequireLoadedClient(Create);`,
157807
+ "",
157808
+ `internal static ${userClassName} RequireInstance() => Instance;`,
157809
+ "",
157810
+ "/// <summary>",
157811
+ "/// Completes once a current store has loaded the user file. Waits for a",
157812
+ "/// store to start loading if none has.",
157813
+ "/// </summary>",
157814
+ `public static Awaitable<${userClassName}> WhenLoadedAsync(System.Threading.CancellationToken cancellationToken = default) => WhenClientLoadedAsync(Create, cancellationToken);`,
157815
+ "",
157816
+ `public ${userApiType} User { get; }`,
157817
+ "",
157818
+ `private ${userClassName}(NeoClient client)`,
157819
+ ` : base(client, ${project}.NeoReadOnlyValueFactories, ${project}.NeoWritableValueFactories)`,
157820
+ "{",
157821
+ ...indent([
157822
+ `Client.RegisterNativeFunctionInvokers(${project}.NativeFunctionInvokers);`,
157823
+ `Client.RegisterDeferredNativeFunctionInvokers(${project}.DeferredNativeFunctionInvokers);`,
157824
+ userType === null ? "User = client.user;" : `User = new ${userType.typeName}(client, client.user, false, NeoValueOwnership.User);`,
157825
+ "Client.StartScriptRuntime();"
157826
+ ]),
157827
+ "}",
157828
+ "",
157829
+ `private static ${userClassName} Create(NeoClient client) => new ${userClassName}(client);`
157830
+ ]),
157831
+ "}"
157832
+ ];
157833
+ }
157101
157834
  function scriptGridClasses(ctx) {
157102
157835
  return ctx.classes.filter(
157103
157836
  (classDef) => hasTileGridContentShape(ctx, classDef.id) && isWorldSystemKind(ctx, classDef.id, NeoWorldSystemClassKind.TileGrid)
@@ -157167,7 +157900,7 @@ function generateNativeFunctionInvokers(ctx) {
157167
157900
  const immediateInvokers = invokers.filter((invoker) => !invoker.deferred);
157168
157901
  const deferredInvokers = invokers.filter((invoker) => invoker.deferred);
157169
157902
  return [
157170
- "private static readonly IReadOnlyDictionary<string, NeoClient.NeoNativeFunctionInvoker> NativeFunctionInvokers =",
157903
+ "internal static readonly IReadOnlyDictionary<string, NeoClient.NeoNativeFunctionInvoker> NativeFunctionInvokers =",
157171
157904
  " new Dictionary<string, NeoClient.NeoNativeFunctionInvoker>",
157172
157905
  " {",
157173
157906
  ...immediateInvokers.flatMap((invoker) => [
@@ -157178,7 +157911,7 @@ function generateNativeFunctionInvokers(ctx) {
157178
157911
  ]),
157179
157912
  " };",
157180
157913
  "",
157181
- "private static readonly IReadOnlyDictionary<string, NeoClient.NeoDeferredNativeFunctionInvoker> DeferredNativeFunctionInvokers =",
157914
+ "internal static readonly IReadOnlyDictionary<string, NeoClient.NeoDeferredNativeFunctionInvoker> DeferredNativeFunctionInvokers =",
157182
157915
  " new Dictionary<string, NeoClient.NeoDeferredNativeFunctionInvoker>",
157183
157916
  " {",
157184
157917
  ...deferredInvokers.flatMap((invoker) => [
@@ -161608,6 +162341,8 @@ function ownershipLiteral(storage) {
161608
162341
  return "NeoValueOwnership.Save";
161609
162342
  case 3 /* Session */:
161610
162343
  return "NeoValueOwnership.Session";
162344
+ case 5 /* User */:
162345
+ return "NeoValueOwnership.User";
161611
162346
  }
161612
162347
  }
161613
162348
  function getConcreteDescendants(ctx, classId) {
@@ -161704,10 +162439,11 @@ function generateSavedConstructor(ctx, classId, className, entries, genericEnv)
161704
162439
  const parameterText = orderedParameters.length > 0 ? `, ${orderedParameters.join(", ")}` : "";
161705
162440
  const argumentText = orderedParameterInfos.map((p) => p.memberName).join(", ");
161706
162441
  const helperArguments = argumentText.length > 0 ? `, ${argumentText}` : "";
161707
- const constructorParameterText = ctx.singleton ? orderedParameters.join(", ") : `${ctx.projectClassName} project${parameterText}`;
161708
- const helperParameterText = ctx.singleton ? orderedParameters.join(", ") : `${ctx.projectClassName} project${parameterText}`;
161709
- const constructorArguments4 = ctx.singleton ? argumentText : `project${helperArguments}`;
161710
- const clientExpression = ctx.singleton ? `${ctx.projectClassName}.RequireInstance().Client` : "project.Client";
162442
+ const client = generatedClientFor(ctx, classId);
162443
+ const constructorParameterText = ctx.singleton ? orderedParameters.join(", ") : `${client.typeName} ${client.parameter}${parameterText}`;
162444
+ const helperParameterText = constructorParameterText;
162445
+ const constructorArguments4 = ctx.singleton ? argumentText : `${client.parameter}${helperArguments}`;
162446
+ const clientExpression = ctx.singleton ? `${client.typeName}.RequireInstance().Client` : `${client.parameter}.Client`;
161711
162447
  return [
161712
162448
  `public ${className}(${constructorParameterText})`,
161713
162449
  ` : this(${clientExpression}, CreateFactoryNode(${constructorArguments4}), false, NeoValueOwnership.Session)`,
@@ -161917,7 +162653,8 @@ function appendedOptionalMemberParameters(ctx, classId, declared, factoryEntries
161917
162653
  return appended;
161918
162654
  }
161919
162655
  function generateDeclaredConstructor(ctx, classId, className, constructorId, parameters, appendedMembers = []) {
161920
- const clientExpression = ctx.singleton ? `${ctx.projectClassName}.RequireInstance().Client` : "project.Client";
162656
+ const client = generatedClientFor(ctx, classId);
162657
+ const clientExpression = ctx.singleton ? `${client.typeName}.RequireInstance().Client` : `${client.parameter}.Client`;
161921
162658
  const declaredParameters = parameters.map(
161922
162659
  (parameter4) => parameter4.defaultLiteral === null ? `${parameter4.csharpType} ${parameter4.identifier}` : `${parameter4.csharpType} ${parameter4.identifier} = ${parameter4.defaultLiteral}`
161923
162660
  );
@@ -161925,7 +162662,9 @@ function generateDeclaredConstructor(ctx, classId, className, constructorId, par
161925
162662
  (member) => `${member.csharpType} ${member.identifier} = null`
161926
162663
  );
161927
162664
  const signatureParameters = [...declaredParameters, ...appendedParameters];
161928
- const parameterText = ctx.singleton ? signatureParameters.join(", ") : [`${ctx.projectClassName} project`, ...signatureParameters].join(", ");
162665
+ const parameterText = ctx.singleton ? signatureParameters.join(", ") : [`${client.typeName} ${client.parameter}`, ...signatureParameters].join(
162666
+ ", "
162667
+ );
161929
162668
  const argumentEntries = parameters.map(
161930
162669
  (parameter4) => `new global::NeoCompose.Runtime.NeoDeclaredConstructorArgument("${escapeCSharpString(parameter4.declaredName)}", ${parameter4.scriptArgument})`
161931
162670
  ).join(", ");
@@ -161961,6 +162700,7 @@ function generateStaticMember(ctx, ownerClass, schemaKey, memberId, genericEnv)
161961
162700
  if (isMemberNSPropertyBase(member)) {
161962
162701
  return generateStaticNSProperty(
161963
162702
  ctx,
162703
+ ownerClass,
161964
162704
  member,
161965
162705
  memberName,
161966
162706
  memberId,
@@ -162057,7 +162797,7 @@ function generateStaticFunctionMethod(ctx, ownerClass, member, genericEnv) {
162057
162797
  if (member.kind === 23 /* NSFunction */) {
162058
162798
  const invocation = callableIsDeferred(member) ? `await node.InvokeStaticAsync(new object?[] ${args})` : `node.InvokeStatic(${immediateInvocationArguments(null, scriptArguments)})`;
162059
162799
  const body = [
162060
- `var client = ${ctx.projectClassName}.RequireInstance().Client;`,
162800
+ `var client = ${generatedClientFor(ctx, ownerClass.id).typeName}.RequireInstance().Client;`,
162061
162801
  `var node = NeoMemberNSFunction.GetOrCreateStatic(client, "${escapeCSharpString(member.id)}");`,
162062
162802
  ...isFunctionVoidTypeInfo(member.returnTypeInfo) ? [`${invocation};`] : [
162063
162803
  `var result = ${invocation};`,
@@ -162079,7 +162819,7 @@ function generateStaticFunctionMethod(ctx, ownerClass, member, genericEnv) {
162079
162819
  declaration,
162080
162820
  "{",
162081
162821
  ...indent([
162082
- `var client = ${ctx.projectClassName}.RequireInstance().Client;`,
162822
+ `var client = ${generatedClientFor(ctx, ownerClass.id).typeName}.RequireInstance().Client;`,
162083
162823
  call2
162084
162824
  ]),
162085
162825
  "}"
@@ -162304,11 +163044,11 @@ function staticDialogueLookupGetter(ctx, member, memberId, storage, saved) {
162304
163044
  ]
162305
163045
  ];
162306
163046
  }
162307
- function generateStaticNSProperty(ctx, member, memberName, memberId, genericEnv) {
163047
+ function generateStaticNSProperty(ctx, ownerClass, member, memberName, memberId, genericEnv) {
162308
163048
  if (!isMemberNSPropertyBase(member)) return [];
162309
163049
  const valueType = csharpValueType(ctx, member, true, genericEnv) ?? "object?";
162310
163050
  const prelude = [
162311
- `var client = ${ctx.projectClassName}.RequireInstance().Client;`,
163051
+ `var client = ${generatedClientFor(ctx, ownerClass.id).typeName}.RequireInstance().Client;`,
162312
163052
  `var node = new NeoMemberNSProperty(client, "${escapeCSharpString(memberId)}", null, NeoValueOwnership.Session);`
162313
163053
  ];
162314
163054
  const getter = [
@@ -162614,17 +163354,18 @@ function claimVariantScopeName(ctx, used, rawName, described, path) {
162614
163354
  });
162615
163355
  return unique;
162616
163356
  }
162617
- function variantResolutionExpression(ctx, targetTypeRef, variantId, lookupValueType) {
163357
+ function variantResolutionExpression(ctx, classId, targetTypeRef, variantId, lookupValueType) {
163358
+ const client = `${generatedClientFor(ctx, classId).typeName}.RequireInstance().Client`;
162618
163359
  if (lookupValueType !== null) {
162619
163360
  return [
162620
163361
  ` NeoGeneratedTypesSupport.ResolveLookupVariant<${targetTypeRef}, ${lookupValueType}>(`,
162621
- ` ${ctx.projectClassName}.RequireInstance().Client,`,
163362
+ ` ${client},`,
162622
163363
  ` "${escapeCSharpString(variantId)}");`
162623
163364
  ];
162624
163365
  }
162625
163366
  return [
162626
163367
  ` NeoGeneratedTypesSupport.ResolveVariant<${targetTypeRef}>(`,
162627
- ` ${ctx.projectClassName}.RequireInstance().Client,`,
163368
+ ` ${client},`,
162628
163369
  ` "${escapeCSharpString(variantId)}");`
162629
163370
  ];
162630
163371
  }
@@ -162671,7 +163412,7 @@ function generateVariantScopeMembers(ctx, node, targetTypeRef, classId, isRootSc
162671
163412
  "/// </summary>",
162672
163413
  `public static NeoVariant<${targetTypeRef}> ${BASE_VARIANT_NAME} =>`,
162673
163414
  ` NeoGeneratedTypesSupport.ResolveBaseVariant<${targetTypeRef}>(`,
162674
- ` ${ctx.projectClassName}.RequireInstance().Client,`,
163415
+ ` ${generatedClientFor(ctx, classId).typeName}.RequireInstance().Client,`,
162675
163416
  ` "${escapeCSharpString(classId)}");`
162676
163417
  );
162677
163418
  }
@@ -162688,6 +163429,7 @@ function generateVariantScopeMembers(ctx, node, targetTypeRef, classId, isRootSc
162688
163429
  `public static ${lookupValueType === null ? `NeoVariant<${targetTypeRef}>` : `NeoLookupVariant<${targetTypeRef}, ${lookupValueType}>`} ${name} =>`,
162689
163430
  ...variantResolutionExpression(
162690
163431
  ctx,
163432
+ classId,
162691
163433
  targetTypeRef,
162692
163434
  variant2.id,
162693
163435
  lookupValueType
@@ -167924,7 +168666,7 @@ var UNITY_CODEGEN_REVISION;
167924
168666
  var init_unity_codegen_revision = __esm({
167925
168667
  "../src/models/exports/unity-codegen-revision.ts"() {
167926
168668
  "use strict";
167927
- UNITY_CODEGEN_REVISION = "30d96d89e3d179ccf291e522834281f90dbd44b464cf51eea76aae87eed97e7e";
168669
+ UNITY_CODEGEN_REVISION = "45a0847addd4616acfc29f142556dcb2e0a27daae14d6aa456430228e66dbc07";
167928
168670
  }
167929
168671
  });
167930
168672
 
@@ -167934,7 +168676,7 @@ var init_project_unity_export_types = __esm({
167934
168676
  "../src/models/exports/project-unity-export-types.ts"() {
167935
168677
  "use strict";
167936
168678
  init_unity_codegen_revision();
167937
- PROJECT_UNITY_EXPORT_SCHEMA_VERSION = 37;
168679
+ PROJECT_UNITY_EXPORT_SCHEMA_VERSION = 38;
167938
168680
  }
167939
168681
  });
167940
168682
 
@@ -177039,7 +177781,7 @@ async function runScript(workspace, command, options, dependencies = {}) {
177039
177781
  }
177040
177782
  }
177041
177783
  function buildStoredNeoScriptChecks(document) {
177042
- const checks = [];
177784
+ const checks2 = [];
177043
177785
  const projectData = document;
177044
177786
  const dialogueEntries = document.dialogueRecords.map((dialogue) => ({
177045
177787
  dialogue,
@@ -177057,7 +177799,7 @@ function buildStoredNeoScriptChecks(document) {
177057
177799
  continue;
177058
177800
  }
177059
177801
  const nodeId = stableRecordId(node, "(unknown-node)");
177060
- checks.push({
177802
+ checks2.push({
177061
177803
  recordKind: "dialogue-node",
177062
177804
  id: nodeId,
177063
177805
  name: nodeId,
@@ -177094,7 +177836,7 @@ function buildStoredNeoScriptChecks(document) {
177094
177836
  value.forEach((conditionValue, index) => {
177095
177837
  if (!isObjectRecord2(conditionValue)) return;
177096
177838
  if (conditionValue.type !== 1 /* Code */) return;
177097
- checks.push({
177839
+ checks2.push({
177098
177840
  recordKind: "dialogue-node",
177099
177841
  id: nodeId,
177100
177842
  name: recordName,
@@ -177127,7 +177869,7 @@ function buildStoredNeoScriptChecks(document) {
177127
177869
  for (const [variableKey, variableValue] of Object.entries(value)) {
177128
177870
  if (!isObjectRecord2(variableValue)) continue;
177129
177871
  const variableId = typeof variableValue.id === "string" ? variableValue.id : variableKey;
177130
- checks.push({
177872
+ checks2.push({
177131
177873
  recordKind: "dialogue-node",
177132
177874
  id: nodeId,
177133
177875
  name: recordName,
@@ -177209,7 +177951,7 @@ function buildStoredNeoScriptChecks(document) {
177209
177951
  const logic = isObjectRecord2(actionValue2.logic) ? actionValue2.logic : null;
177210
177952
  if (logic?.type !== 1 /* Code */) return;
177211
177953
  const actionId = stableRecordId(actionValue2, `action-${actionIndex}`);
177212
- checks.push({
177954
+ checks2.push({
177213
177955
  recordKind: "dialogue-node",
177214
177956
  id: nodeId,
177215
177957
  name: recordName,
@@ -177260,7 +178002,7 @@ function buildStoredNeoScriptChecks(document) {
177260
178002
  group.conditions.forEach((conditionValue, index) => {
177261
178003
  if (!isObjectRecord2(conditionValue)) return;
177262
178004
  if (conditionValue.type !== 1 /* Code */) return;
177263
- checks.push({
178005
+ checks2.push({
177264
178006
  recordKind: "dialogue-group",
177265
178007
  id: groupId,
177266
178008
  name: groupName,
@@ -177294,7 +178036,7 @@ function buildStoredNeoScriptChecks(document) {
177294
178036
  for (const migration of document.migrations) {
177295
178037
  const migrationId = stableRecordId(migration, "(unknown-migration)");
177296
178038
  const migrationName = typeof migration.name === "string" ? migration.name : migrationId;
177297
- checks.push({
178039
+ checks2.push({
177298
178040
  recordKind: "migration",
177299
178041
  id: migrationId,
177300
178042
  name: migrationName,
@@ -177319,7 +178061,7 @@ function buildStoredNeoScriptChecks(document) {
177319
178061
  }
177320
178062
  });
177321
178063
  }
177322
- return checks;
178064
+ return checks2;
177323
178065
  }
177324
178066
  function buildScriptDialogueContext(dialogue, nodes) {
177325
178067
  const trigger = nodes.find((node) => node.type === 0 /* Trigger */);
@@ -184082,6 +184824,7 @@ var init_PreviewDocumentIndex = __esm({
184082
184824
  "../src/runtime/dialogue-preview/PreviewDocumentIndex.ts"() {
184083
184825
  "use strict";
184084
184826
  init_value_row_owner_members();
184827
+ init_user_safe_types();
184085
184828
  init_core();
184086
184829
  init_generics();
184087
184830
  init_read_class_member();
@@ -184528,7 +185271,65 @@ var init_PreviewDocumentIndex = __esm({
184528
185271
  }
184529
185272
  return ids;
184530
185273
  }
185274
+ /** P104 §3.1. Previews run save-side, so User data is read-only. */
185275
+ assertNotUserData(valueId) {
185276
+ if (this.isUserRow(valueId)) {
185277
+ throw new Error(USER_DATA_WRITE_ERROR);
185278
+ }
185279
+ }
185280
+ /**
185281
+ * P104 §3.3. Save data holds no User class instance and no reference to a
185282
+ * User row, directly or through a closure's captures. `referenceId` maps a
185283
+ * captured runtime value to the row it names.
185284
+ */
185285
+ assertSaveRow(row, referenceId4) {
185286
+ const classId = row.classId;
185287
+ if (typeof classId === "string" && this.storage.allowedStorage(classId) === 5 /* User */) {
185288
+ const className = this.input.document.classes.find((entry) => entry.id === classId)?.name ?? classId;
185289
+ throw new Error(
185290
+ `\`${className}\` is a User class, so Save data can't hold it.`
185291
+ );
185292
+ }
185293
+ const userRowId = this.userRowInlinedBy(row.value, referenceId4);
185294
+ if (userRowId === null) return;
185295
+ throw new Error(
185296
+ `Save data can't hold a reference to \`root.User\` data. \`${this.field(userRowId)?.path ?? userRowId}\` is under \`root.User\`.`
185297
+ );
185298
+ }
185299
+ /** A delegate row's value, or the inline delegates in a sparse row's entries. */
185300
+ userRowInlinedBy(value, referenceId4) {
185301
+ const direct = this.userRowReferencedBy(value, referenceId4, 0);
185302
+ if (direct !== null || value === null || typeof value !== "object")
185303
+ return direct;
185304
+ for (const entry of Object.values(value)) {
185305
+ const inlined = this.userRowReferencedBy(entry, referenceId4, 0);
185306
+ if (inlined !== null) return inlined;
185307
+ }
185308
+ return null;
185309
+ }
185310
+ userRowReferencedBy(value, referenceId4, depth) {
185311
+ if (isMemberDelegateTarget(value))
185312
+ return value.valueId !== null && this.isUserRow(value.valueId) ? value.valueId : null;
185313
+ if (isNSActionValue(value)) {
185314
+ for (const listener of value.listeners)
185315
+ if (listener.valueId !== null && this.isUserRow(listener.valueId))
185316
+ return listener.valueId;
185317
+ return null;
185318
+ }
185319
+ if (!isNSDelegateClosureValue(value) || depth > 64) return null;
185320
+ for (const capture of value.captures ?? []) {
185321
+ const nested = this.userRowReferencedBy(capture, referenceId4, depth + 1);
185322
+ if (nested !== null) return nested;
185323
+ const id2 = referenceId4(capture);
185324
+ if (id2 !== null && this.isUserRow(id2)) return id2;
185325
+ }
185326
+ return null;
185327
+ }
185328
+ isUserRow(valueId) {
185329
+ return this.field(valueId)?.storage === 5 /* User */;
185330
+ }
184531
185331
  assertWritable(valueId) {
185332
+ this.assertNotUserData(valueId);
184532
185333
  const field = this.field(valueId);
184533
185334
  if (field?.storage === 2 /* Save */ || field?.storage === 3 /* Session */)
184534
185335
  return;
@@ -184633,15 +185434,16 @@ var init_dialogue_dryrun_listeners = __esm({
184633
185434
  return void 0;
184634
185435
  return this.runtime.sourcesForCopy(ids);
184635
185436
  };
185437
+ context.assertValueWritable = (id2) => this.index.assertNotUserData(id2);
185438
+ context.assertActionInvocable = (id2) => this.index.assertNotUserData(id2);
184636
185439
  return context;
184637
185440
  }
184638
185441
  commit(result, context, apply) {
184639
185442
  this.context = context;
184640
- if (!this.hasDefaults && !this.runtime.hasOverrides && !evaluatedWrites(result).some((write) => write.kind === "changeListener")) {
184641
- apply(result, context);
184642
- return;
184643
- }
184644
185443
  apply(result, context);
185444
+ this.assertSaveWrites(result, context);
185445
+ if (!this.hasDefaults && !this.runtime.hasOverrides && !evaluatedWrites(result).some((write) => write.kind === "changeListener"))
185446
+ return;
184645
185447
  context.vm = this.world.vm;
184646
185448
  context.rootValue = this.world.rootValue;
184647
185449
  this.copied = context.__copiedChangeListeners ?? this.copied;
@@ -184660,6 +185462,35 @@ var init_dialogue_dryrun_listeners = __esm({
184660
185462
  if (listenerWrites.length || adopted.size)
184661
185463
  this.runtime.prepare(listenerWrites, adopted).publish();
184662
185464
  }
185465
+ /** P104 §3.3. The written Save rows hold no User instance or reference. */
185466
+ assertSaveWrites(result, context) {
185467
+ const referenceId4 = (value) => inspectNeoScriptValueReference(context, value)?.row.id ?? null;
185468
+ const checked = /* @__PURE__ */ new Set();
185469
+ const check2 = (id2) => {
185470
+ if (checked.has(id2)) return;
185471
+ checked.add(id2);
185472
+ if (this.index.field(id2)?.storage !== 2 /* Save */) return;
185473
+ const row = readNeoScriptRuntimeRow(context, id2);
185474
+ if (row) this.index.assertSaveRow(row, referenceId4);
185475
+ };
185476
+ for (const write of evaluatedWrites(result)) {
185477
+ if (write.kind === "changeListener") continue;
185478
+ const destination = write.resolvedDestination;
185479
+ if (destination !== void 0 && "containerValueId" in destination) {
185480
+ const container = readNeoScriptRuntimeRow(
185481
+ context,
185482
+ destination.containerValueId
185483
+ );
185484
+ check2(destination.containerValueId);
185485
+ const value = container?.value;
185486
+ const slot = destination.key === void 0 || typeof value !== "object" || value === null ? void 0 : Reflect.get(value, destination.key);
185487
+ if (typeof slot === "string") check2(slot);
185488
+ }
185489
+ if (!write.valueId) continue;
185490
+ check2(write.valueId);
185491
+ for (const id2 of this.index.descendantIds(write.valueId)) check2(id2);
185492
+ }
185493
+ }
184663
185494
  };
184664
185495
  }
184665
185496
  });
@@ -186267,7 +187098,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
186267
187098
  async function main() {
186268
187099
  const args = parseArgs(process.argv.slice(2));
186269
187100
  if (args.command === "--version") {
186270
- console.log("0.71.4");
187101
+ console.log("0.72.0");
186271
187102
  return;
186272
187103
  }
186273
187104
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {