@neocompose/cli 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/neo.mjs +2284 -243
- package/package.json +1 -1
- package/skill/SKILL.md +0 -147
package/dist/neo.mjs
CHANGED
|
@@ -278,7 +278,40 @@ var init_project_db_response_types = __esm({
|
|
|
278
278
|
}
|
|
279
279
|
});
|
|
280
280
|
|
|
281
|
+
// ../src/models/attributes/attribute-storage.ts
|
|
282
|
+
function isAttributeStorage(value) {
|
|
283
|
+
if (typeof value !== "string") return false;
|
|
284
|
+
return ATTRIBUTE_STORAGE_VALUES.has(value);
|
|
285
|
+
}
|
|
286
|
+
function isEffectiveAttributeStorage(value) {
|
|
287
|
+
if (!isAttributeStorage(value)) return false;
|
|
288
|
+
return value !== "inherit" /* Inherit */;
|
|
289
|
+
}
|
|
290
|
+
var AttributeStorage, ATTRIBUTE_STORAGE_VALUES;
|
|
291
|
+
var init_attribute_storage = __esm({
|
|
292
|
+
"../src/models/attributes/attribute-storage.ts"() {
|
|
293
|
+
"use strict";
|
|
294
|
+
AttributeStorage = /* @__PURE__ */ ((AttributeStorage3) => {
|
|
295
|
+
AttributeStorage3["Inherit"] = "inherit";
|
|
296
|
+
AttributeStorage3["Static"] = "static";
|
|
297
|
+
AttributeStorage3["Save"] = "save";
|
|
298
|
+
AttributeStorage3["Session"] = "session";
|
|
299
|
+
return AttributeStorage3;
|
|
300
|
+
})(AttributeStorage || {});
|
|
301
|
+
ATTRIBUTE_STORAGE_VALUES = new Set(
|
|
302
|
+
Object.values(AttributeStorage)
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
|
|
281
307
|
// ../src/models/project/project-root-attributes.ts
|
|
308
|
+
function buildRootStorageMap(project) {
|
|
309
|
+
const map = /* @__PURE__ */ new Map();
|
|
310
|
+
for (const root of PROJECT_ROOTS) {
|
|
311
|
+
map.set(project[root.projectField], root.storage);
|
|
312
|
+
}
|
|
313
|
+
return map;
|
|
314
|
+
}
|
|
282
315
|
function projectRootAttributesInDisplayOrder(project) {
|
|
283
316
|
return PROJECT_ROOTS.map((root) => ({
|
|
284
317
|
root,
|
|
@@ -289,27 +322,28 @@ var PROJECT_ROOTS;
|
|
|
289
322
|
var init_project_root_attributes = __esm({
|
|
290
323
|
"../src/models/project/project-root-attributes.ts"() {
|
|
291
324
|
"use strict";
|
|
325
|
+
init_attribute_storage();
|
|
292
326
|
PROJECT_ROOTS = [
|
|
293
327
|
{
|
|
294
328
|
key: "Assets",
|
|
295
329
|
choice: "assets",
|
|
296
330
|
projectField: "rootAssetsAttributeId",
|
|
297
331
|
label: "Assets",
|
|
298
|
-
|
|
332
|
+
storage: "static" /* Static */
|
|
299
333
|
},
|
|
300
334
|
{
|
|
301
335
|
key: "Save",
|
|
302
336
|
choice: "save",
|
|
303
337
|
projectField: "rootSaveFileAttributeId",
|
|
304
338
|
label: "Save",
|
|
305
|
-
|
|
339
|
+
storage: "save" /* Save */
|
|
306
340
|
},
|
|
307
341
|
{
|
|
308
342
|
key: "Session",
|
|
309
343
|
choice: "session",
|
|
310
344
|
projectField: "rootSessionAttributeId",
|
|
311
345
|
label: "Session",
|
|
312
|
-
|
|
346
|
+
storage: "session" /* Session */
|
|
313
347
|
}
|
|
314
348
|
];
|
|
315
349
|
}
|
|
@@ -516,6 +550,29 @@ function readWorkspaceConfig(root) {
|
|
|
516
550
|
`"${NEO_CONFIG_FILE}" is missing string field "apiBaseUrl".`
|
|
517
551
|
);
|
|
518
552
|
}
|
|
553
|
+
const profile = config.profile === "release" ? "release" : "editor";
|
|
554
|
+
const convexUrl = typeof config.convexUrl === "string" ? config.convexUrl : void 0;
|
|
555
|
+
if (typeof config.unityConfigPath === "string") {
|
|
556
|
+
if (config.projectId !== void 0) {
|
|
557
|
+
throw new Error(
|
|
558
|
+
`"${NEO_CONFIG_FILE}" sets "unityConfigPath", so "projectId" must be removed \u2014 the Unity config asset is the single source of truth.`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
if (config.versionId !== void 0) {
|
|
562
|
+
throw new Error(
|
|
563
|
+
`"${NEO_CONFIG_FILE}" sets "unityConfigPath", so "versionId" must be removed \u2014 the Unity config asset is the single source of truth.`
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
const ids = readUnityConfigIds(root, config.unityConfigPath);
|
|
567
|
+
return {
|
|
568
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
569
|
+
projectId: ids.projectId,
|
|
570
|
+
versionId: ids.versionId,
|
|
571
|
+
profile,
|
|
572
|
+
convexUrl,
|
|
573
|
+
unityConfigPath: config.unityConfigPath
|
|
574
|
+
};
|
|
575
|
+
}
|
|
519
576
|
if (typeof config.projectId !== "string") {
|
|
520
577
|
throw new Error(
|
|
521
578
|
`"${NEO_CONFIG_FILE}" is missing string field "projectId".`
|
|
@@ -526,23 +583,79 @@ function readWorkspaceConfig(root) {
|
|
|
526
583
|
`"${NEO_CONFIG_FILE}" is missing string field "versionId".`
|
|
527
584
|
);
|
|
528
585
|
}
|
|
529
|
-
const profile = config.profile === "release" ? "release" : "editor";
|
|
530
586
|
return {
|
|
531
587
|
apiBaseUrl: config.apiBaseUrl,
|
|
532
588
|
projectId: config.projectId,
|
|
533
589
|
versionId: config.versionId,
|
|
534
590
|
profile,
|
|
535
|
-
convexUrl
|
|
591
|
+
convexUrl
|
|
536
592
|
};
|
|
537
593
|
}
|
|
538
594
|
function writeWorkspaceConfig(root, config) {
|
|
595
|
+
let persisted = { ...config };
|
|
596
|
+
if (config.unityConfigPath !== void 0) {
|
|
597
|
+
const { projectId, versionId, ...rest } = persisted;
|
|
598
|
+
void projectId;
|
|
599
|
+
void versionId;
|
|
600
|
+
persisted = rest;
|
|
601
|
+
writeUnityConfigVersionId(root, config.unityConfigPath, config.versionId);
|
|
602
|
+
}
|
|
539
603
|
writeFileSync2(
|
|
540
604
|
join2(root, NEO_CONFIG_FILE),
|
|
541
|
-
`${JSON.stringify(
|
|
605
|
+
`${JSON.stringify(persisted, null, 2)}
|
|
542
606
|
`,
|
|
543
607
|
"utf8"
|
|
544
608
|
);
|
|
545
609
|
}
|
|
610
|
+
function readUnityConfigIds(root, unityConfigPath) {
|
|
611
|
+
const assetPath = resolve(root, unityConfigPath);
|
|
612
|
+
if (!existsSync2(assetPath)) {
|
|
613
|
+
throw new Error(
|
|
614
|
+
`"unityConfigPath" in "${NEO_CONFIG_FILE}" points to "${assetPath}", which does not exist.`
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
const content = readFileSync2(assetPath, "utf8");
|
|
618
|
+
const projectId = matchUnityScalarField(content, "projectId");
|
|
619
|
+
if (projectId === null) {
|
|
620
|
+
throw new Error(
|
|
621
|
+
`Unity config asset "${assetPath}" has no "projectId" field.`
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
const versionId = matchUnityScalarField(content, "versionId");
|
|
625
|
+
if (versionId === null) {
|
|
626
|
+
throw new Error(
|
|
627
|
+
`Unity config asset "${assetPath}" has no "versionId" field.`
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
return { projectId, versionId };
|
|
631
|
+
}
|
|
632
|
+
function writeUnityConfigVersionId(root, unityConfigPath, versionId) {
|
|
633
|
+
const assetPath = resolve(root, unityConfigPath);
|
|
634
|
+
if (!existsSync2(assetPath)) {
|
|
635
|
+
throw new Error(
|
|
636
|
+
`"unityConfigPath" in "${NEO_CONFIG_FILE}" points to "${assetPath}", which does not exist.`
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
const content = readFileSync2(assetPath, "utf8");
|
|
640
|
+
const current = matchUnityScalarField(content, "versionId");
|
|
641
|
+
if (current === null) {
|
|
642
|
+
throw new Error(
|
|
643
|
+
`Unity config asset "${assetPath}" has no "versionId" field to update.`
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
if (current === versionId) return;
|
|
647
|
+
const updated = content.replace(
|
|
648
|
+
/^(\s*versionId:[ \t]*).*$/m,
|
|
649
|
+
`$1${versionId}`
|
|
650
|
+
);
|
|
651
|
+
writeFileSync2(assetPath, updated, "utf8");
|
|
652
|
+
}
|
|
653
|
+
function matchUnityScalarField(content, field) {
|
|
654
|
+
const match = new RegExp(`^\\s*${field}:[ \\t]*(\\S+)[ \\t]*$`, "m").exec(
|
|
655
|
+
content
|
|
656
|
+
);
|
|
657
|
+
return match === null ? null : match[1];
|
|
658
|
+
}
|
|
546
659
|
function readWorkspaceState(root) {
|
|
547
660
|
const statePath = join2(root, NEO_STATE_DIR, NEO_STATE_FILE);
|
|
548
661
|
if (!existsSync2(statePath)) {
|
|
@@ -713,6 +826,11 @@ function isNSTypeInfoAsset(value) {
|
|
|
713
826
|
const t = value.type;
|
|
714
827
|
return t === 11 /* Sprite */ || t === 12 /* Audio */;
|
|
715
828
|
}
|
|
829
|
+
function isNSTypeInfoVector(value) {
|
|
830
|
+
if (!isNSTypeInfoBase(value)) return false;
|
|
831
|
+
const t = value.type;
|
|
832
|
+
return t === 14 /* Vector2 */ || t === 15 /* Vector2Int */ || t === 16 /* Vector3 */ || t === 17 /* Vector3Int */;
|
|
833
|
+
}
|
|
716
834
|
function isNSTypeInfoUnknown(value) {
|
|
717
835
|
const v = value;
|
|
718
836
|
return typeof v === "object" && v !== null && v.type === NS_TYPE_UNKNOWN && typeof v.required === "boolean";
|
|
@@ -748,7 +866,7 @@ function isNSTypeInfoEnum(value) {
|
|
|
748
866
|
return typeof v.enumId === "string" && v.enumId.length > 0;
|
|
749
867
|
}
|
|
750
868
|
function isNSTypeInfo(value) {
|
|
751
|
-
return isNSTypeInfoUnknown(value) || isNSTypeInfoPrimitive(value) || isNSTypeInfoAsset(value) || isNSTypeInfoCustom(value) || isNSTypeInfoCollection(value) || isNSTypeInfoLookup(value) || isNSTypeInfoEnum(value);
|
|
869
|
+
return isNSTypeInfoUnknown(value) || isNSTypeInfoPrimitive(value) || isNSTypeInfoAsset(value) || isNSTypeInfoVector(value) || isNSTypeInfoCustom(value) || isNSTypeInfoCollection(value) || isNSTypeInfoLookup(value) || isNSTypeInfoEnum(value);
|
|
752
870
|
}
|
|
753
871
|
function isNSArgumentTypeInfo(value) {
|
|
754
872
|
return isRequiredType(value, isNSArgumentTypeInfoUnsafe);
|
|
@@ -824,6 +942,59 @@ function isAttributeString(value) {
|
|
|
824
942
|
function isAttributeFloatBase(value) {
|
|
825
943
|
return asTypedAttributeBase(value, 4 /* Float */) !== null;
|
|
826
944
|
}
|
|
945
|
+
function hasExactKeys(value, keys) {
|
|
946
|
+
const actual = Object.keys(value);
|
|
947
|
+
if (actual.length !== keys.length) return false;
|
|
948
|
+
return keys.every((key) => Object.prototype.hasOwnProperty.call(value, key));
|
|
949
|
+
}
|
|
950
|
+
function isFiniteNumberValue(value) {
|
|
951
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
952
|
+
}
|
|
953
|
+
function isVectorComponentValue(value, integer) {
|
|
954
|
+
if (!isFiniteNumberValue(value)) return false;
|
|
955
|
+
if (!integer) return true;
|
|
956
|
+
return Number.isInteger(value);
|
|
957
|
+
}
|
|
958
|
+
function isVector2ValueWithComponents(value, integer) {
|
|
959
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
960
|
+
return false;
|
|
961
|
+
}
|
|
962
|
+
const record = value;
|
|
963
|
+
if (!hasExactKeys(record, ["x", "y"])) return false;
|
|
964
|
+
return isVectorComponentValue(record.x, integer) && isVectorComponentValue(record.y, integer);
|
|
965
|
+
}
|
|
966
|
+
function isVector3ValueWithComponents(value, integer) {
|
|
967
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
968
|
+
return false;
|
|
969
|
+
}
|
|
970
|
+
const record = value;
|
|
971
|
+
if (!hasExactKeys(record, ["x", "y", "z"])) return false;
|
|
972
|
+
return isVectorComponentValue(record.x, integer) && isVectorComponentValue(record.y, integer) && isVectorComponentValue(record.z, integer);
|
|
973
|
+
}
|
|
974
|
+
function isVector2Value(value) {
|
|
975
|
+
return isVector2ValueWithComponents(value, false);
|
|
976
|
+
}
|
|
977
|
+
function isVector2IntValue(value) {
|
|
978
|
+
return isVector2ValueWithComponents(value, true);
|
|
979
|
+
}
|
|
980
|
+
function isVector3Value(value) {
|
|
981
|
+
return isVector3ValueWithComponents(value, false);
|
|
982
|
+
}
|
|
983
|
+
function isVector3IntValue(value) {
|
|
984
|
+
return isVector3ValueWithComponents(value, true);
|
|
985
|
+
}
|
|
986
|
+
function isAttributeVector2Base(value) {
|
|
987
|
+
return asTypedAttributeBase(value, 14 /* Vector2 */) !== null;
|
|
988
|
+
}
|
|
989
|
+
function isAttributeVector2IntBase(value) {
|
|
990
|
+
return asTypedAttributeBase(value, 15 /* Vector2Int */) !== null;
|
|
991
|
+
}
|
|
992
|
+
function isAttributeVector3Base(value) {
|
|
993
|
+
return asTypedAttributeBase(value, 16 /* Vector3 */) !== null;
|
|
994
|
+
}
|
|
995
|
+
function isAttributeVector3IntBase(value) {
|
|
996
|
+
return asTypedAttributeBase(value, 17 /* Vector3Int */) !== null;
|
|
997
|
+
}
|
|
827
998
|
function isAttributeDictionaryBase(value) {
|
|
828
999
|
const v = asTypedAttributeBase(value, 5 /* Dictionary */);
|
|
829
1000
|
if (v === null) return false;
|
|
@@ -931,13 +1102,25 @@ function isAttributeFunctionBase(value) {
|
|
|
931
1102
|
}
|
|
932
1103
|
return true;
|
|
933
1104
|
}
|
|
1105
|
+
function attributeTypeSupportsStorage(type) {
|
|
1106
|
+
if (type === 10 /* NSGetter */) return false;
|
|
1107
|
+
return type !== 13 /* Function */;
|
|
1108
|
+
}
|
|
1109
|
+
function hasValidStorageField(v) {
|
|
1110
|
+
if (v.storage === void 0) return true;
|
|
1111
|
+
if (v.storage === null) return true;
|
|
1112
|
+
if (!isAttributeStorage(v.storage)) return false;
|
|
1113
|
+
if (typeof v.type !== "number") return false;
|
|
1114
|
+
return attributeTypeSupportsStorage(v.type);
|
|
1115
|
+
}
|
|
934
1116
|
function isAttributeBase(value) {
|
|
935
1117
|
const v = value;
|
|
936
|
-
const matchesConcreteType = isAttributeNullBase(value) || isAttributeBoolBase(value) || isAttributeIntBase(value) || isAttributeStringBase(value) || isAttributeFloatBase(value) || isAttributeDictionaryBase(value) || isAttributeListBase(value) || isAttributeCustomBase(value) || isAttributeEnumBase(value) || isAttributeLookupBase(value) || isAttributeNSGetterBase(value) || isAttributeSpriteBase(value) || isAttributeAudioBase(value) || isAttributeFunctionBase(value);
|
|
1118
|
+
const matchesConcreteType = isAttributeNullBase(value) || isAttributeBoolBase(value) || isAttributeIntBase(value) || isAttributeStringBase(value) || isAttributeFloatBase(value) || isAttributeDictionaryBase(value) || isAttributeListBase(value) || isAttributeCustomBase(value) || isAttributeEnumBase(value) || isAttributeLookupBase(value) || isAttributeNSGetterBase(value) || isAttributeSpriteBase(value) || isAttributeAudioBase(value) || isAttributeFunctionBase(value) || isAttributeVector2Base(value) || isAttributeVector2IntBase(value) || isAttributeVector3Base(value) || isAttributeVector3IntBase(value);
|
|
937
1119
|
if (!matchesConcreteType) return false;
|
|
938
1120
|
if (typeof v?.name !== "string") return false;
|
|
939
1121
|
if (typeof v.locked !== "boolean") return false;
|
|
940
1122
|
if (typeof v.required !== "boolean") return false;
|
|
1123
|
+
if (!hasValidStorageField(v)) return false;
|
|
941
1124
|
if (v.system !== void 0 && v.system !== null) {
|
|
942
1125
|
return isSystemMetadata(v.system);
|
|
943
1126
|
}
|
|
@@ -959,6 +1142,7 @@ var init_attribute_types = __esm({
|
|
|
959
1142
|
"use strict";
|
|
960
1143
|
init_core();
|
|
961
1144
|
init_neoscript();
|
|
1145
|
+
init_attribute_storage();
|
|
962
1146
|
AttributeType = /* @__PURE__ */ ((AttributeType9) => {
|
|
963
1147
|
AttributeType9[AttributeType9["Null"] = 0] = "Null";
|
|
964
1148
|
AttributeType9[AttributeType9["Bool"] = 1] = "Bool";
|
|
@@ -974,6 +1158,10 @@ var init_attribute_types = __esm({
|
|
|
974
1158
|
AttributeType9[AttributeType9["Sprite"] = 11] = "Sprite";
|
|
975
1159
|
AttributeType9[AttributeType9["Audio"] = 12] = "Audio";
|
|
976
1160
|
AttributeType9[AttributeType9["Function"] = 13] = "Function";
|
|
1161
|
+
AttributeType9[AttributeType9["Vector2"] = 14] = "Vector2";
|
|
1162
|
+
AttributeType9[AttributeType9["Vector2Int"] = 15] = "Vector2Int";
|
|
1163
|
+
AttributeType9[AttributeType9["Vector3"] = 16] = "Vector3";
|
|
1164
|
+
AttributeType9[AttributeType9["Vector3Int"] = 17] = "Vector3Int";
|
|
977
1165
|
return AttributeType9;
|
|
978
1166
|
})(AttributeType || {});
|
|
979
1167
|
FunctionAttributeNamePattern = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
@@ -1010,7 +1198,7 @@ var init_attribute_types_db_response = __esm({
|
|
|
1010
1198
|
// ../src/models/custom-types/custom-types.ts
|
|
1011
1199
|
function isCustomTypeBase(value) {
|
|
1012
1200
|
const v = value;
|
|
1013
|
-
return typeof v?.name === "string" && isCustomTypeSchema(v?.schema) && isOptionalSchemaKeyOrder(v?.schemaKeyOrder) && typeof v?.hiddenInAttributeSelector === "boolean" && typeof v?.isAbstract === "boolean" && (v.system === void 0 || v.system === null || isSystemMetadata(v.system));
|
|
1201
|
+
return typeof v?.name === "string" && isCustomTypeSchema(v?.schema) && isOptionalSchemaKeyOrder(v?.schemaKeyOrder) && typeof v?.hiddenInAttributeSelector === "boolean" && typeof v?.isAbstract === "boolean" && (v.allowedStorage === void 0 || v.allowedStorage === null || isEffectiveAttributeStorage(v.allowedStorage)) && (v.system === void 0 || v.system === null || isSystemMetadata(v.system));
|
|
1014
1202
|
}
|
|
1015
1203
|
function getCustomTypeSchemaKeyOrder(type) {
|
|
1016
1204
|
const schemaKeys = Object.keys(type.schema);
|
|
@@ -1046,6 +1234,7 @@ var init_custom_types = __esm({
|
|
|
1046
1234
|
"../src/models/custom-types/custom-types.ts"() {
|
|
1047
1235
|
"use strict";
|
|
1048
1236
|
init_core();
|
|
1237
|
+
init_attribute_storage();
|
|
1049
1238
|
}
|
|
1050
1239
|
});
|
|
1051
1240
|
|
|
@@ -1645,6 +1834,165 @@ var init_build_default_attribute_value = __esm({
|
|
|
1645
1834
|
}
|
|
1646
1835
|
});
|
|
1647
1836
|
|
|
1837
|
+
// ../src/models/attributes/effective-storage.ts
|
|
1838
|
+
function declaredAttributeStorage(attributeId, attributes) {
|
|
1839
|
+
const declared = walkExtendsAttributeChain(attributeId, attributes, (a) => {
|
|
1840
|
+
const storage = a.storage;
|
|
1841
|
+
return isAttributeStorage(storage) ? storage : void 0;
|
|
1842
|
+
});
|
|
1843
|
+
return declared ?? "inherit" /* Inherit */;
|
|
1844
|
+
}
|
|
1845
|
+
var EffectiveStorageResolver;
|
|
1846
|
+
var init_effective_storage = __esm({
|
|
1847
|
+
"../src/models/attributes/effective-storage.ts"() {
|
|
1848
|
+
"use strict";
|
|
1849
|
+
init_attribute_storage();
|
|
1850
|
+
init_attribute_types();
|
|
1851
|
+
init_structural_narrow();
|
|
1852
|
+
init_custom_types();
|
|
1853
|
+
init_inheritance();
|
|
1854
|
+
EffectiveStorageResolver = class {
|
|
1855
|
+
constructor(ctx) {
|
|
1856
|
+
this.ctx = ctx;
|
|
1857
|
+
for (const attribute of ctx.attributes) {
|
|
1858
|
+
this.attributeById.set(attribute.id, attribute);
|
|
1859
|
+
}
|
|
1860
|
+
this.buildParentEdges();
|
|
1861
|
+
}
|
|
1862
|
+
ctx;
|
|
1863
|
+
attributeById = /* @__PURE__ */ new Map();
|
|
1864
|
+
parentIdsByAttributeId = /* @__PURE__ */ new Map();
|
|
1865
|
+
mergedSchemaByTypeId = /* @__PURE__ */ new Map();
|
|
1866
|
+
effectiveByAttributeId = /* @__PURE__ */ new Map();
|
|
1867
|
+
resolving = /* @__PURE__ */ new Set();
|
|
1868
|
+
/** Declared storage of `attributeId`, resolved through the extends chain. */
|
|
1869
|
+
declaredStorage(attributeId) {
|
|
1870
|
+
return declaredAttributeStorage(attributeId, this.ctx.attributes);
|
|
1871
|
+
}
|
|
1872
|
+
/** Placement-parent attribute ids of `attributeId` (empty for roots). */
|
|
1873
|
+
parentAttributeIds(attributeId) {
|
|
1874
|
+
if (this.ctx.rootStorage.has(attributeId)) return [];
|
|
1875
|
+
return this.parentIdsByAttributeId.get(attributeId) ?? [];
|
|
1876
|
+
}
|
|
1877
|
+
/**
|
|
1878
|
+
* The set of effective storages of `attributeId` across every placement
|
|
1879
|
+
* (specs/attribute-storage.md §2.3, generalized to the placement *graph*).
|
|
1880
|
+
*/
|
|
1881
|
+
effectiveStorages(attributeId) {
|
|
1882
|
+
const cached = this.effectiveByAttributeId.get(attributeId);
|
|
1883
|
+
if (cached !== void 0) return cached;
|
|
1884
|
+
if (this.resolving.has(attributeId)) {
|
|
1885
|
+
return /* @__PURE__ */ new Set();
|
|
1886
|
+
}
|
|
1887
|
+
this.resolving.add(attributeId);
|
|
1888
|
+
try {
|
|
1889
|
+
const result = /* @__PURE__ */ new Set();
|
|
1890
|
+
const rootStorage = this.ctx.rootStorage.get(attributeId);
|
|
1891
|
+
if (rootStorage !== void 0) {
|
|
1892
|
+
result.add(rootStorage);
|
|
1893
|
+
} else {
|
|
1894
|
+
const declared = this.declaredStorage(attributeId);
|
|
1895
|
+
if (declared !== "inherit" /* Inherit */) {
|
|
1896
|
+
result.add(declared);
|
|
1897
|
+
} else {
|
|
1898
|
+
for (const parentId of this.parentAttributeIds(attributeId)) {
|
|
1899
|
+
for (const storage of this.effectiveStorages(parentId)) {
|
|
1900
|
+
result.add(storage);
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
this.effectiveByAttributeId.set(attributeId, result);
|
|
1906
|
+
return result;
|
|
1907
|
+
} finally {
|
|
1908
|
+
this.resolving.delete(attributeId);
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
/**
|
|
1912
|
+
* Effective storage for one concrete placement chain, ordered root-first
|
|
1913
|
+
* (the project root at index 0, the target attribute last). Used when the
|
|
1914
|
+
* caller knows the exact path (NeoScript member walks, value trees).
|
|
1915
|
+
*/
|
|
1916
|
+
effectiveStorageForPath(attributeIdsRootToLeaf) {
|
|
1917
|
+
for (let i = attributeIdsRootToLeaf.length - 1; i >= 0; i--) {
|
|
1918
|
+
const attributeId = attributeIdsRootToLeaf[i];
|
|
1919
|
+
const rootStorage = this.ctx.rootStorage.get(attributeId);
|
|
1920
|
+
if (rootStorage !== void 0 && i === 0) return rootStorage;
|
|
1921
|
+
const declared = this.declaredStorage(attributeId);
|
|
1922
|
+
if (declared !== "inherit" /* Inherit */) return declared;
|
|
1923
|
+
}
|
|
1924
|
+
const rootId = attributeIdsRootToLeaf[0];
|
|
1925
|
+
const anchored = rootId === void 0 ? void 0 : this.ctx.rootStorage.get(rootId);
|
|
1926
|
+
if (anchored !== void 0) return anchored;
|
|
1927
|
+
throw new Error(
|
|
1928
|
+
`Storage path did not resolve: no attribute in the chain declares a storage class and the chain head "${rootId ?? "(empty)"}" is not a stamped project root.`
|
|
1929
|
+
);
|
|
1930
|
+
}
|
|
1931
|
+
/** Custom type's merged (inheritance-resolved) schema, memoized. */
|
|
1932
|
+
mergedSchema(typeId) {
|
|
1933
|
+
const cached = this.mergedSchemaByTypeId.get(typeId);
|
|
1934
|
+
if (cached !== void 0) return cached;
|
|
1935
|
+
const chain = resolveInheritanceChain(typeId, this.ctx.types);
|
|
1936
|
+
const merged = chain.length === 0 ? [] : mergeSchemas(chain);
|
|
1937
|
+
this.mergedSchemaByTypeId.set(typeId, merged);
|
|
1938
|
+
return merged;
|
|
1939
|
+
}
|
|
1940
|
+
/** Resolved per-type custom field — `customTypeId` through extends chain. */
|
|
1941
|
+
resolvedCustomTypeId(attributeId) {
|
|
1942
|
+
return walkExtendsAttributeChain(
|
|
1943
|
+
attributeId,
|
|
1944
|
+
this.ctx.attributes,
|
|
1945
|
+
(a) => getOptionalString(a, "customTypeId")
|
|
1946
|
+
);
|
|
1947
|
+
}
|
|
1948
|
+
/** Resolved `entryAttributeId` through the extends chain. */
|
|
1949
|
+
resolvedEntryAttributeId(attributeId) {
|
|
1950
|
+
return walkExtendsAttributeChain(
|
|
1951
|
+
attributeId,
|
|
1952
|
+
this.ctx.attributes,
|
|
1953
|
+
(a) => getOptionalString(a, "entryAttributeId")
|
|
1954
|
+
);
|
|
1955
|
+
}
|
|
1956
|
+
/** Resolved `collectionAttributeId` through the extends chain. */
|
|
1957
|
+
resolvedCollectionAttributeId(attributeId) {
|
|
1958
|
+
return walkExtendsAttributeChain(
|
|
1959
|
+
attributeId,
|
|
1960
|
+
this.ctx.attributes,
|
|
1961
|
+
(a) => getOptionalString(a, "collectionAttributeId")
|
|
1962
|
+
);
|
|
1963
|
+
}
|
|
1964
|
+
attribute(attributeId) {
|
|
1965
|
+
return this.attributeById.get(attributeId);
|
|
1966
|
+
}
|
|
1967
|
+
buildParentEdges() {
|
|
1968
|
+
const addEdge = (childId, parentId) => {
|
|
1969
|
+
const existing = this.parentIdsByAttributeId.get(childId);
|
|
1970
|
+
if (existing === void 0) {
|
|
1971
|
+
this.parentIdsByAttributeId.set(childId, [parentId]);
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
if (!existing.includes(parentId)) existing.push(parentId);
|
|
1975
|
+
};
|
|
1976
|
+
for (const attribute of this.ctx.attributes) {
|
|
1977
|
+
if (attribute.type === 7 /* Custom */) {
|
|
1978
|
+
const typeId = this.resolvedCustomTypeId(attribute.id);
|
|
1979
|
+
if (typeId !== void 0) {
|
|
1980
|
+
for (const entry of this.mergedSchema(typeId)) {
|
|
1981
|
+
addEdge(entry.attributeId, attribute.id);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
continue;
|
|
1985
|
+
}
|
|
1986
|
+
if (attribute.type === 6 /* List */ || attribute.type === 5 /* Dictionary */) {
|
|
1987
|
+
const entryId = this.resolvedEntryAttributeId(attribute.id);
|
|
1988
|
+
if (entryId !== void 0) addEdge(entryId, attribute.id);
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
});
|
|
1995
|
+
|
|
1648
1996
|
// ../src/models/attributes/value-responses.ts
|
|
1649
1997
|
var init_value_responses = __esm({
|
|
1650
1998
|
"../src/models/attributes/value-responses.ts"() {
|
|
@@ -1678,9 +2026,11 @@ var init_delete_attribute_response = __esm({
|
|
|
1678
2026
|
var init_attributes = __esm({
|
|
1679
2027
|
"../src/models/attributes/index.ts"() {
|
|
1680
2028
|
"use strict";
|
|
2029
|
+
init_attribute_storage();
|
|
1681
2030
|
init_attribute_types();
|
|
1682
2031
|
init_attribute_types_db_response();
|
|
1683
2032
|
init_build_default_attribute_value();
|
|
2033
|
+
init_effective_storage();
|
|
1684
2034
|
init_value_responses();
|
|
1685
2035
|
init_edit_attribute_props();
|
|
1686
2036
|
init_delete_attribute_response();
|
|
@@ -1747,6 +2097,7 @@ var init_localization_types = __esm({
|
|
|
1747
2097
|
var init_localization_export = __esm({
|
|
1748
2098
|
"../src/models/localization/localization-export.ts"() {
|
|
1749
2099
|
"use strict";
|
|
2100
|
+
init_localization_types();
|
|
1750
2101
|
}
|
|
1751
2102
|
});
|
|
1752
2103
|
|
|
@@ -2102,7 +2453,11 @@ var init_projection = __esm({
|
|
|
2102
2453
|
NSGetter: 10,
|
|
2103
2454
|
Sprite: 11,
|
|
2104
2455
|
Audio: 12,
|
|
2105
|
-
Function: 13
|
|
2456
|
+
Function: 13,
|
|
2457
|
+
Vector2: 14,
|
|
2458
|
+
Vector2Int: 15,
|
|
2459
|
+
Vector3: 16,
|
|
2460
|
+
Vector3Int: 17
|
|
2106
2461
|
};
|
|
2107
2462
|
ATTRIBUTE_CLASS_BY_TYPE = {
|
|
2108
2463
|
[ATTRIBUTE_TYPE.Null]: "NeoNull",
|
|
@@ -2118,7 +2473,11 @@ var init_projection = __esm({
|
|
|
2118
2473
|
[ATTRIBUTE_TYPE.NSGetter]: "NeoGetter",
|
|
2119
2474
|
[ATTRIBUTE_TYPE.Sprite]: "NeoSprite",
|
|
2120
2475
|
[ATTRIBUTE_TYPE.Audio]: "NeoAudio",
|
|
2121
|
-
[ATTRIBUTE_TYPE.Function]: "NeoFunction"
|
|
2476
|
+
[ATTRIBUTE_TYPE.Function]: "NeoFunction",
|
|
2477
|
+
[ATTRIBUTE_TYPE.Vector2]: "NeoVector2",
|
|
2478
|
+
[ATTRIBUTE_TYPE.Vector2Int]: "NeoVector2Int",
|
|
2479
|
+
[ATTRIBUTE_TYPE.Vector3]: "NeoVector3",
|
|
2480
|
+
[ATTRIBUTE_TYPE.Vector3Int]: "NeoVector3Int"
|
|
2122
2481
|
};
|
|
2123
2482
|
ATTRIBUTE_TYPE_BY_CLASS = new Map(
|
|
2124
2483
|
Object.entries(ATTRIBUTE_CLASS_BY_TYPE).map(([type, name]) => [
|
|
@@ -2836,6 +3195,18 @@ function reconstructTypeFile(parsed, typeIdsByName, enumIdsByName, baseRecords,
|
|
|
2836
3195
|
data.name = namedString(marker, "Name", parsed.file) ?? declaration.name;
|
|
2837
3196
|
data.isAbstract = declaration.isAbstract;
|
|
2838
3197
|
data.hiddenInAttributeSelector = namedBoolean(marker, "Hidden", parsed.file) ?? false;
|
|
3198
|
+
const allowedStorage = namedString(marker, "AllowedStorage", parsed.file);
|
|
3199
|
+
if (allowedStorage !== void 0) {
|
|
3200
|
+
if (allowedStorage !== "static" && allowedStorage !== "save" && allowedStorage !== "session") {
|
|
3201
|
+
throw new SchemaParseError(
|
|
3202
|
+
`AllowedStorage must be "static", "save", or "session"; found "${allowedStorage}".`,
|
|
3203
|
+
parsed.file,
|
|
3204
|
+
marker.line,
|
|
3205
|
+
marker.column
|
|
3206
|
+
);
|
|
3207
|
+
}
|
|
3208
|
+
data.allowedStorage = allowedStorage;
|
|
3209
|
+
}
|
|
2839
3210
|
const extendsTypeIdArg = namedString(marker, "ExtendsTypeId", parsed.file);
|
|
2840
3211
|
if (extendsTypeIdArg !== void 0) {
|
|
2841
3212
|
data.extendsTypeId = extendsTypeIdArg;
|
|
@@ -2959,6 +3330,18 @@ function reconstructProperty(file, property, typeIdsByName, enumIdsByName, baseR
|
|
|
2959
3330
|
data.name = namedString(marker, "Name", file) ?? key;
|
|
2960
3331
|
data.required = !property.nullable;
|
|
2961
3332
|
data.locked = namedBoolean(marker, "Locked", file) ?? false;
|
|
3333
|
+
const storage = namedString(marker, "Storage", file);
|
|
3334
|
+
if (storage !== void 0) {
|
|
3335
|
+
if (storage !== "static" && storage !== "save" && storage !== "session") {
|
|
3336
|
+
throw new SchemaParseError(
|
|
3337
|
+
`Storage must be "static", "save", or "session"; found "${storage}".`,
|
|
3338
|
+
file,
|
|
3339
|
+
marker.line,
|
|
3340
|
+
marker.column
|
|
3341
|
+
);
|
|
3342
|
+
}
|
|
3343
|
+
data.storage = storage;
|
|
3344
|
+
}
|
|
2962
3345
|
const extendsId = namedString(marker, "ExtendsId", file);
|
|
2963
3346
|
if (extendsId !== void 0) data.extendsAttributeId = extendsId;
|
|
2964
3347
|
const defaultJson = parseJsonArg(marker, "DefaultJson", file);
|
|
@@ -2990,6 +3373,11 @@ function reconstructProperty(file, property, typeIdsByName, enumIdsByName, baseR
|
|
|
2990
3373
|
if (localizable !== void 0) data.localizable = localizable;
|
|
2991
3374
|
break;
|
|
2992
3375
|
}
|
|
3376
|
+
case ATTRIBUTE_TYPE.Vector2:
|
|
3377
|
+
case ATTRIBUTE_TYPE.Vector2Int:
|
|
3378
|
+
case ATTRIBUTE_TYPE.Vector3:
|
|
3379
|
+
case ATTRIBUTE_TYPE.Vector3Int:
|
|
3380
|
+
break;
|
|
2993
3381
|
case ATTRIBUTE_TYPE.Dictionary:
|
|
2994
3382
|
case ATTRIBUTE_TYPE.List: {
|
|
2995
3383
|
const entryChainJson = parseJsonArg(marker, "EntryChainJson", file);
|
|
@@ -3275,6 +3663,14 @@ function shapeToTrivialFields(shape, typeIdsByName, enumIdsByName, property, fil
|
|
|
3275
3663
|
return { type: ATTRIBUTE_TYPE.String };
|
|
3276
3664
|
case "float":
|
|
3277
3665
|
return { type: ATTRIBUTE_TYPE.Float };
|
|
3666
|
+
case "NeoVector2":
|
|
3667
|
+
return { type: ATTRIBUTE_TYPE.Vector2 };
|
|
3668
|
+
case "NeoVector2Int":
|
|
3669
|
+
return { type: ATTRIBUTE_TYPE.Vector2Int };
|
|
3670
|
+
case "NeoVector3":
|
|
3671
|
+
return { type: ATTRIBUTE_TYPE.Vector3 };
|
|
3672
|
+
case "NeoVector3Int":
|
|
3673
|
+
return { type: ATTRIBUTE_TYPE.Vector3Int };
|
|
3278
3674
|
case "IReadOnlyList":
|
|
3279
3675
|
return { type: ATTRIBUTE_TYPE.List };
|
|
3280
3676
|
case "IReadOnlyDictionary":
|
|
@@ -3686,6 +4082,12 @@ function emitTypeFile(context, typeRecord, warnings, reachableAttributeIds) {
|
|
|
3686
4082
|
delete remaining.hiddenInAttributeSelector;
|
|
3687
4083
|
if (data.hiddenInAttributeSelector) args.push("Hidden = true");
|
|
3688
4084
|
}
|
|
4085
|
+
if (typeof data.allowedStorage === "string") {
|
|
4086
|
+
delete remaining.allowedStorage;
|
|
4087
|
+
args.push(`AllowedStorage = ${csString(data.allowedStorage)}`);
|
|
4088
|
+
} else if (data.allowedStorage === null) {
|
|
4089
|
+
delete remaining.allowedStorage;
|
|
4090
|
+
}
|
|
3689
4091
|
const modifier = data.isAbstract === true ? "abstract" : "sealed";
|
|
3690
4092
|
if (typeof data.isAbstract === "boolean") delete remaining.isAbstract;
|
|
3691
4093
|
const schema = isObjectRecord2(data.schema) ? data.schema : {};
|
|
@@ -3822,6 +4224,12 @@ function emitProperty(context, schemaKey, attributeRecord, memberNames, warnings
|
|
|
3822
4224
|
delete remaining.locked;
|
|
3823
4225
|
if (data.locked) args.push("Locked = true");
|
|
3824
4226
|
}
|
|
4227
|
+
if (typeof data.storage === "string" && data.storage !== "inherit") {
|
|
4228
|
+
delete remaining.storage;
|
|
4229
|
+
args.push(`Storage = ${csString(data.storage)}`);
|
|
4230
|
+
} else if (data.storage === "inherit") {
|
|
4231
|
+
delete remaining.storage;
|
|
4232
|
+
}
|
|
3825
4233
|
const required = data.required === true;
|
|
3826
4234
|
if (typeof data.required === "boolean") delete remaining.required;
|
|
3827
4235
|
let csType = "object";
|
|
@@ -3865,6 +4273,18 @@ function emitProperty(context, schemaKey, attributeRecord, memberNames, warnings
|
|
|
3865
4273
|
args.push(`Localizable = ${data.localizable ? "true" : "false"}`);
|
|
3866
4274
|
}
|
|
3867
4275
|
break;
|
|
4276
|
+
case ATTRIBUTE_TYPE.Vector2:
|
|
4277
|
+
csType = "NeoVector2";
|
|
4278
|
+
break;
|
|
4279
|
+
case ATTRIBUTE_TYPE.Vector2Int:
|
|
4280
|
+
csType = "NeoVector2Int";
|
|
4281
|
+
break;
|
|
4282
|
+
case ATTRIBUTE_TYPE.Vector3:
|
|
4283
|
+
csType = "NeoVector3";
|
|
4284
|
+
break;
|
|
4285
|
+
case ATTRIBUTE_TYPE.Vector3Int:
|
|
4286
|
+
csType = "NeoVector3Int";
|
|
4287
|
+
break;
|
|
3868
4288
|
case ATTRIBUTE_TYPE.Dictionary:
|
|
3869
4289
|
case ATTRIBUTE_TYPE.List: {
|
|
3870
4290
|
const entryAttributeId = data.entryAttributeId;
|
|
@@ -4052,6 +4472,14 @@ function renderEntryChainType(context, chain, index) {
|
|
|
4052
4472
|
return "string";
|
|
4053
4473
|
case ATTRIBUTE_TYPE.Float:
|
|
4054
4474
|
return "float";
|
|
4475
|
+
case ATTRIBUTE_TYPE.Vector2:
|
|
4476
|
+
return "NeoVector2";
|
|
4477
|
+
case ATTRIBUTE_TYPE.Vector2Int:
|
|
4478
|
+
return "NeoVector2Int";
|
|
4479
|
+
case ATTRIBUTE_TYPE.Vector3:
|
|
4480
|
+
return "NeoVector3";
|
|
4481
|
+
case ATTRIBUTE_TYPE.Vector3Int:
|
|
4482
|
+
return "NeoVector3Int";
|
|
4055
4483
|
case ATTRIBUTE_TYPE.Dictionary:
|
|
4056
4484
|
return `IReadOnlyDictionary<string, ${renderEntryChainType(context, chain, index + 1)}>`;
|
|
4057
4485
|
case ATTRIBUTE_TYPE.List:
|
|
@@ -6129,6 +6557,43 @@ function resolveAssetMemberType(receiverType, memberName) {
|
|
|
6129
6557
|
}
|
|
6130
6558
|
return null;
|
|
6131
6559
|
}
|
|
6560
|
+
function resolveVectorMemberType(receiverType, memberName) {
|
|
6561
|
+
const componentType = receiverType.type === 15 /* Vector2Int */ || receiverType.type === 17 /* Vector3Int */ ? 2 /* Int */ : receiverType.type === 14 /* Vector2 */ || receiverType.type === 16 /* Vector3 */ ? 4 /* Float */ : null;
|
|
6562
|
+
if (componentType === null) return null;
|
|
6563
|
+
if (memberName === "x" || memberName === "y") {
|
|
6564
|
+
return { type: componentType, required: true };
|
|
6565
|
+
}
|
|
6566
|
+
if (memberName === "z" && (receiverType.type === 16 /* Vector3 */ || receiverType.type === 17 /* Vector3Int */)) {
|
|
6567
|
+
return { type: componentType, required: true };
|
|
6568
|
+
}
|
|
6569
|
+
return null;
|
|
6570
|
+
}
|
|
6571
|
+
function vectorTypeForConstructorName(name) {
|
|
6572
|
+
switch (name) {
|
|
6573
|
+
case "Vector2":
|
|
6574
|
+
return 14 /* Vector2 */;
|
|
6575
|
+
case "Vector2Int":
|
|
6576
|
+
return 15 /* Vector2Int */;
|
|
6577
|
+
case "Vector3":
|
|
6578
|
+
return 16 /* Vector3 */;
|
|
6579
|
+
case "Vector3Int":
|
|
6580
|
+
return 17 /* Vector3Int */;
|
|
6581
|
+
default:
|
|
6582
|
+
return null;
|
|
6583
|
+
}
|
|
6584
|
+
}
|
|
6585
|
+
function vectorComponentCount(type) {
|
|
6586
|
+
if (type === 14 /* Vector2 */ || type === 15 /* Vector2Int */) {
|
|
6587
|
+
return 2;
|
|
6588
|
+
}
|
|
6589
|
+
return 3;
|
|
6590
|
+
}
|
|
6591
|
+
function vectorComponentType(type) {
|
|
6592
|
+
if (type === 15 /* Vector2Int */ || type === 17 /* Vector3Int */) {
|
|
6593
|
+
return 2 /* Int */;
|
|
6594
|
+
}
|
|
6595
|
+
return 4 /* Float */;
|
|
6596
|
+
}
|
|
6132
6597
|
function describeType(t) {
|
|
6133
6598
|
const opt = t.required ? "" : "?";
|
|
6134
6599
|
switch (t.type) {
|
|
@@ -6148,6 +6613,14 @@ function describeType(t) {
|
|
|
6148
6613
|
return `SpriteInfo${opt}`;
|
|
6149
6614
|
case 12 /* Audio */:
|
|
6150
6615
|
return `AudioClipInfo${opt}`;
|
|
6616
|
+
case 14 /* Vector2 */:
|
|
6617
|
+
return `Vector2${opt}`;
|
|
6618
|
+
case 15 /* Vector2Int */:
|
|
6619
|
+
return `Vector2Int${opt}`;
|
|
6620
|
+
case 16 /* Vector3 */:
|
|
6621
|
+
return `Vector3${opt}`;
|
|
6622
|
+
case 17 /* Vector3Int */:
|
|
6623
|
+
return `Vector3Int${opt}`;
|
|
6151
6624
|
case 7 /* Custom */:
|
|
6152
6625
|
return `Custom<${t.typeId}>${opt}`;
|
|
6153
6626
|
case 8 /* Enum */:
|
|
@@ -6697,6 +7170,9 @@ var init_resolver = __esm({
|
|
|
6697
7170
|
* (bool for Where/Find, inferred for Select).
|
|
6698
7171
|
*/
|
|
6699
7172
|
expectedReturnStack = [];
|
|
7173
|
+
/** Lazy effective-storage machinery for writability classification. */
|
|
7174
|
+
storageResolver = null;
|
|
7175
|
+
rootStorageByAttributeId = null;
|
|
6700
7176
|
compile(body) {
|
|
6701
7177
|
const scope = new Scope(null);
|
|
6702
7178
|
scope.define({
|
|
@@ -6851,8 +7327,9 @@ var init_resolver = __esm({
|
|
|
6851
7327
|
}
|
|
6852
7328
|
case "return": {
|
|
6853
7329
|
const expectedReturn = this.currentExpectedReturn();
|
|
7330
|
+
const inActionBody = this.ctx.mode === "action" /* Action */ && this.lambdaDepth === 0;
|
|
6854
7331
|
if (stmt.expr === null) {
|
|
6855
|
-
if (
|
|
7332
|
+
if (inActionBody) {
|
|
6856
7333
|
return {
|
|
6857
7334
|
type: "return" /* return */,
|
|
6858
7335
|
pointer: null
|
|
@@ -6870,7 +7347,7 @@ var init_resolver = __esm({
|
|
|
6870
7347
|
};
|
|
6871
7348
|
}
|
|
6872
7349
|
const resolved = this.resolveExpr(stmt.expr, scope, expectedReturn);
|
|
6873
|
-
if (
|
|
7350
|
+
if (inActionBody) {
|
|
6874
7351
|
if (resolved.type.type !== 0 /* Null */) {
|
|
6875
7352
|
throw new CompileError(
|
|
6876
7353
|
`Action functions can only return null; got ${describeType(resolved.type)}`,
|
|
@@ -7444,70 +7921,153 @@ var init_resolver = __esm({
|
|
|
7444
7921
|
pos
|
|
7445
7922
|
);
|
|
7446
7923
|
}
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7924
|
+
const rooted = this.resolveRootedTargetStorage(expr, scope);
|
|
7925
|
+
if (rooted !== null) {
|
|
7926
|
+
if (rooted.storage === "save" /* Save */) {
|
|
7927
|
+
return rooted.reRootedFromStatic ? "assetToSaveLookup" /* AssetToSaveLookup */ : "save" /* Save */;
|
|
7928
|
+
}
|
|
7929
|
+
if (rooted.storage === "session" /* Session */) {
|
|
7930
|
+
return rooted.reRootedFromStatic ? "assetToSessionLookup" /* AssetToSessionLookup */ : "session" /* Session */;
|
|
7931
|
+
}
|
|
7932
|
+
throw new CompileError(
|
|
7933
|
+
`Cannot assign to '${path}' because its effective storage is static.`,
|
|
7934
|
+
pos
|
|
7457
7935
|
);
|
|
7458
|
-
|
|
7936
|
+
}
|
|
7937
|
+
const declared = this.declaredStorageForMemberTarget(expr, scope, pos);
|
|
7938
|
+
if (declared === "static" /* Static */) {
|
|
7459
7939
|
throw new CompileError(
|
|
7460
|
-
`Cannot assign to '${path}' because
|
|
7940
|
+
`Cannot assign to '${path}' because its effective storage is static.`,
|
|
7461
7941
|
pos
|
|
7462
7942
|
);
|
|
7463
7943
|
}
|
|
7944
|
+
if (declared === "session" /* Session */) return "session" /* Session */;
|
|
7464
7945
|
return "save" /* Save */;
|
|
7465
7946
|
}
|
|
7466
|
-
|
|
7467
|
-
|
|
7947
|
+
/**
|
|
7948
|
+
* Placement-based storage resolution for an assignment target rooted at a
|
|
7949
|
+
* `root.<Member>` path (specs/attribute-storage.md §5.1). Returns null for
|
|
7950
|
+
* targets not rooted at `root` — their placement is runtime-determined.
|
|
7951
|
+
*
|
|
7952
|
+
* Walks the member/index chain root-first, tracking the effective storage
|
|
7953
|
+
* context: each hop's declared storage (resolved through the extends
|
|
7954
|
+
* chain) replaces the context; Lookup hops on a Static context re-root the
|
|
7955
|
+
* context at the target collection's effective storage and mark the
|
|
7956
|
+
* `AssetTo*Lookup` classification (§2.4 / legacy
|
|
7957
|
+
* `rootAssetsPathCrossesWritableLookup` semantics).
|
|
7958
|
+
*/
|
|
7959
|
+
resolveRootedTargetStorage(expr, scope) {
|
|
7960
|
+
if (expr.kind === "index") {
|
|
7961
|
+
const receiver = this.resolveRootedTargetStorage(expr.receiver, scope);
|
|
7962
|
+
if (receiver === null) return null;
|
|
7963
|
+
let storage2 = receiver.storage;
|
|
7964
|
+
let entryAttributeId = null;
|
|
7965
|
+
if (receiver.attributeId !== null) {
|
|
7966
|
+
const storageResolver2 = this.getStorageResolver();
|
|
7967
|
+
const directEntryId = storageResolver2.resolvedEntryAttributeId(
|
|
7968
|
+
receiver.attributeId
|
|
7969
|
+
);
|
|
7970
|
+
const lookupCollectionId = directEntryId === void 0 ? storageResolver2.resolvedCollectionAttributeId(
|
|
7971
|
+
receiver.attributeId
|
|
7972
|
+
) : void 0;
|
|
7973
|
+
const entryId = directEntryId ?? (lookupCollectionId === void 0 ? void 0 : storageResolver2.resolvedEntryAttributeId(lookupCollectionId));
|
|
7974
|
+
if (entryId !== void 0) {
|
|
7975
|
+
entryAttributeId = entryId;
|
|
7976
|
+
const declared2 = storageResolver2.declaredStorage(entryId);
|
|
7977
|
+
if (declared2 !== "inherit" /* Inherit */) storage2 = declared2;
|
|
7978
|
+
}
|
|
7979
|
+
}
|
|
7980
|
+
return {
|
|
7981
|
+
storage: storage2,
|
|
7982
|
+
reRootedFromStatic: receiver.reRootedFromStatic,
|
|
7983
|
+
attributeId: entryAttributeId
|
|
7984
|
+
};
|
|
7985
|
+
}
|
|
7986
|
+
if (expr.kind !== "member") return null;
|
|
7987
|
+
if (expr.receiver.kind === "ident" && expr.receiver.name === "root") {
|
|
7988
|
+
const rootId = this.rootAttributeIdForMember(expr.name);
|
|
7989
|
+
if (rootId === null) return null;
|
|
7990
|
+
const storage2 = this.getRootStorageMap().get(rootId);
|
|
7991
|
+
if (storage2 === void 0) return null;
|
|
7992
|
+
return { storage: storage2, reRootedFromStatic: false, attributeId: rootId };
|
|
7993
|
+
}
|
|
7994
|
+
const parent = this.resolveRootedTargetStorage(expr.receiver, scope);
|
|
7995
|
+
if (parent === null) return null;
|
|
7468
7996
|
const attr = this.resolveMemberAttribute(
|
|
7469
7997
|
expr.receiver,
|
|
7470
7998
|
expr.name,
|
|
7471
7999
|
scope,
|
|
7472
|
-
pos
|
|
8000
|
+
expr.pos
|
|
7473
8001
|
);
|
|
7474
|
-
if (attr
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
8002
|
+
if (attr === null) {
|
|
8003
|
+
return {
|
|
8004
|
+
storage: parent.storage,
|
|
8005
|
+
reRootedFromStatic: parent.reRootedFromStatic,
|
|
8006
|
+
attributeId: null
|
|
8007
|
+
};
|
|
8008
|
+
}
|
|
8009
|
+
const storageResolver = this.getStorageResolver();
|
|
8010
|
+
let storage = parent.storage;
|
|
8011
|
+
const declared = storageResolver.declaredStorage(attr.id);
|
|
8012
|
+
if (declared !== "inherit" /* Inherit */) storage = declared;
|
|
8013
|
+
let reRootedFromStatic = parent.reRootedFromStatic;
|
|
8014
|
+
if (storage === "static" /* Static */ && isAttributeLookupBase(attr)) {
|
|
8015
|
+
const collectionId = storageResolver.resolvedCollectionAttributeId(
|
|
8016
|
+
attr.id
|
|
8017
|
+
);
|
|
8018
|
+
if (collectionId !== void 0) {
|
|
8019
|
+
const targetStorage = this.lookupTargetStorage(collectionId);
|
|
8020
|
+
if (targetStorage === "save" /* Save */ || targetStorage === "session" /* Session */) {
|
|
8021
|
+
storage = targetStorage;
|
|
8022
|
+
reRootedFromStatic = true;
|
|
8023
|
+
}
|
|
8024
|
+
}
|
|
8025
|
+
}
|
|
8026
|
+
return { storage, reRootedFromStatic, attributeId: attr.id };
|
|
8027
|
+
}
|
|
8028
|
+
/**
|
|
8029
|
+
* Effective storage of a lookup's target collection across its placements.
|
|
8030
|
+
* Save wins over Session when a collection is placed in both (mirrors the
|
|
8031
|
+
* legacy save-root-first `attributeBelongsToRoot` check order).
|
|
8032
|
+
*/
|
|
8033
|
+
lookupTargetStorage(collectionAttributeId) {
|
|
8034
|
+
const set = this.getStorageResolver().effectiveStorages(
|
|
8035
|
+
collectionAttributeId
|
|
7478
8036
|
);
|
|
8037
|
+
if (set.has("save" /* Save */)) return "save" /* Save */;
|
|
8038
|
+
if (set.has("session" /* Session */)) return "session" /* Session */;
|
|
8039
|
+
if (set.has("static" /* Static */)) return "static" /* Static */;
|
|
8040
|
+
return null;
|
|
7479
8041
|
}
|
|
7480
|
-
|
|
7481
|
-
|
|
7482
|
-
|
|
7483
|
-
|
|
7484
|
-
|
|
7485
|
-
|
|
8042
|
+
/**
|
|
8043
|
+
* Declared storage (through the extends chain) of the final member
|
|
8044
|
+
* attribute of a non-rooted assignment target, or `Inherit` when the
|
|
8045
|
+
* target is not a member access / cannot be resolved.
|
|
8046
|
+
*/
|
|
8047
|
+
declaredStorageForMemberTarget(expr, scope, pos) {
|
|
8048
|
+
if (expr.kind !== "member") return "inherit" /* Inherit */;
|
|
8049
|
+
const attr = this.resolveMemberAttribute(
|
|
7486
8050
|
expr.receiver,
|
|
7487
|
-
|
|
8051
|
+
expr.name,
|
|
8052
|
+
scope,
|
|
8053
|
+
pos
|
|
7488
8054
|
);
|
|
7489
|
-
if (
|
|
8055
|
+
if (attr === null) return "inherit" /* Inherit */;
|
|
8056
|
+
return this.getStorageResolver().declaredStorage(attr.id);
|
|
8057
|
+
}
|
|
8058
|
+
validateNonGetterAssignmentTarget(expr, scope, pos) {
|
|
8059
|
+
if (expr.kind !== "member") return;
|
|
7490
8060
|
const attr = this.resolveMemberAttribute(
|
|
7491
8061
|
expr.receiver,
|
|
7492
8062
|
expr.name,
|
|
7493
8063
|
scope,
|
|
7494
|
-
|
|
8064
|
+
pos
|
|
8065
|
+
);
|
|
8066
|
+
if (attr?.type !== 10 /* NSGetter */) return;
|
|
8067
|
+
throw new CompileError(
|
|
8068
|
+
`Cannot assign to getter attribute '${attr.name}' because getter attributes are read-only.`,
|
|
8069
|
+
pos
|
|
7495
8070
|
);
|
|
7496
|
-
if (!attr) return null;
|
|
7497
|
-
if (!isAttributeLookupBase(attr)) return null;
|
|
7498
|
-
if (this.attributeBelongsToRoot(
|
|
7499
|
-
this.ctx.project.rootSaveFileAttributeId,
|
|
7500
|
-
attr.collectionAttributeId
|
|
7501
|
-
)) {
|
|
7502
|
-
return "assetToSaveLookup" /* AssetToSaveLookup */;
|
|
7503
|
-
}
|
|
7504
|
-
if (this.attributeBelongsToRoot(
|
|
7505
|
-
this.ctx.project.rootSessionAttributeId,
|
|
7506
|
-
attr.collectionAttributeId
|
|
7507
|
-
)) {
|
|
7508
|
-
return "assetToSessionLookup" /* AssetToSessionLookup */;
|
|
7509
|
-
}
|
|
7510
|
-
return null;
|
|
7511
8071
|
}
|
|
7512
8072
|
resolveMemberAttribute(receiverAst, name, scope, pos) {
|
|
7513
8073
|
const receiver = this.resolveExpr(receiverAst, scope);
|
|
@@ -7529,45 +8089,28 @@ var init_resolver = __esm({
|
|
|
7529
8089
|
if (!attributeId) return null;
|
|
7530
8090
|
return this.ctx.attributes.find((a) => a.id === attributeId) ?? null;
|
|
7531
8091
|
}
|
|
7532
|
-
attributeBelongsToRoot(rootAttributeId, targetId) {
|
|
7533
|
-
const visited = /* @__PURE__ */ new Set();
|
|
7534
|
-
const stack = [rootAttributeId];
|
|
7535
|
-
while (stack.length > 0) {
|
|
7536
|
-
const attrId = stack.pop();
|
|
7537
|
-
if (!attrId) continue;
|
|
7538
|
-
if (attrId === targetId) return true;
|
|
7539
|
-
if (visited.has(attrId)) continue;
|
|
7540
|
-
visited.add(attrId);
|
|
7541
|
-
const attr = this.ctx.attributes.find((a) => a.id === attrId);
|
|
7542
|
-
if (!attr) continue;
|
|
7543
|
-
if (isAttributeCustomBase(attr)) {
|
|
7544
|
-
const customType = this.ctx.types.find(
|
|
7545
|
-
(t) => t.id === attr.customTypeId
|
|
7546
|
-
);
|
|
7547
|
-
if (!customType) continue;
|
|
7548
|
-
for (const entry of mergeSchemas(
|
|
7549
|
-
resolveInheritanceChain(customType.id, this.ctx.types)
|
|
7550
|
-
)) {
|
|
7551
|
-
if (entry.attributeId !== null) stack.push(entry.attributeId);
|
|
7552
|
-
}
|
|
7553
|
-
continue;
|
|
7554
|
-
}
|
|
7555
|
-
if (isAttributeListBase(attr) || isAttributeDictionaryBase(attr)) {
|
|
7556
|
-
stack.push(attr.entryAttributeId);
|
|
7557
|
-
continue;
|
|
7558
|
-
}
|
|
7559
|
-
if (isAttributeLookupBase(attr)) {
|
|
7560
|
-
stack.push(attr.collectionAttributeId);
|
|
7561
|
-
}
|
|
7562
|
-
}
|
|
7563
|
-
return false;
|
|
7564
|
-
}
|
|
7565
8092
|
rootAttributeIdForMember(name) {
|
|
7566
8093
|
const root = projectRootAttributesInDisplayOrder(this.ctx.project).find(
|
|
7567
8094
|
(entry) => entry.root.key === name
|
|
7568
8095
|
);
|
|
7569
8096
|
return root?.attributeId ?? null;
|
|
7570
8097
|
}
|
|
8098
|
+
getRootStorageMap() {
|
|
8099
|
+
if (this.rootStorageByAttributeId === null) {
|
|
8100
|
+
this.rootStorageByAttributeId = buildRootStorageMap(this.ctx.project);
|
|
8101
|
+
}
|
|
8102
|
+
return this.rootStorageByAttributeId;
|
|
8103
|
+
}
|
|
8104
|
+
getStorageResolver() {
|
|
8105
|
+
if (this.storageResolver === null) {
|
|
8106
|
+
this.storageResolver = new EffectiveStorageResolver({
|
|
8107
|
+
attributes: this.ctx.attributes,
|
|
8108
|
+
types: this.ctx.types,
|
|
8109
|
+
rootStorage: this.getRootStorageMap()
|
|
8110
|
+
});
|
|
8111
|
+
}
|
|
8112
|
+
return this.storageResolver;
|
|
8113
|
+
}
|
|
7571
8114
|
invalidateNarrowingForAssignment(target, scope) {
|
|
7572
8115
|
const path = canonicalPath(target);
|
|
7573
8116
|
if (!path) return;
|
|
@@ -7749,6 +8292,20 @@ var init_resolver = __esm({
|
|
|
7749
8292
|
type: optional ? withRequired(memberType, false) : memberType
|
|
7750
8293
|
};
|
|
7751
8294
|
}
|
|
8295
|
+
const vectorMemberType = resolveVectorMemberType(receiver.type, name);
|
|
8296
|
+
if (vectorMemberType !== null) {
|
|
8297
|
+
return {
|
|
8298
|
+
pointer: {
|
|
8299
|
+
type: "keyOf" /* keyOf */,
|
|
8300
|
+
keyOf: {
|
|
8301
|
+
pointer: receiver.pointer,
|
|
8302
|
+
key: stringPointer(name)
|
|
8303
|
+
},
|
|
8304
|
+
...optional ? { optional: true } : {}
|
|
8305
|
+
},
|
|
8306
|
+
type: optional ? withRequired(vectorMemberType, false) : vectorMemberType
|
|
8307
|
+
};
|
|
8308
|
+
}
|
|
7752
8309
|
if (receiver.type.type === 7 /* Custom */) {
|
|
7753
8310
|
const typeId = receiver.type.typeId;
|
|
7754
8311
|
if (typeId === ROOT_TYPE_ID) {
|
|
@@ -8202,6 +8759,10 @@ var init_resolver = __esm({
|
|
|
8202
8759
|
);
|
|
8203
8760
|
}
|
|
8204
8761
|
resolveGlobalCall(name, args, scope, pos) {
|
|
8762
|
+
const vectorType = vectorTypeForConstructorName(name);
|
|
8763
|
+
if (vectorType !== null) {
|
|
8764
|
+
return this.resolveVectorConstructorCall(vectorType, args, scope, pos);
|
|
8765
|
+
}
|
|
8205
8766
|
const functionType = name === "VisitCount" ? "visitCount" /* visitCount */ : name === "HasVisited" ? "hasVisited" /* hasVisited */ : null;
|
|
8206
8767
|
if (!functionType) {
|
|
8207
8768
|
throw new CompileError(`Unknown function '${name}'`, pos);
|
|
@@ -8230,20 +8791,53 @@ var init_resolver = __esm({
|
|
|
8230
8791
|
type: functionType === "visitCount" /* visitCount */ ? { type: 2 /* Int */, required: true } : { type: 1 /* Bool */, required: true }
|
|
8231
8792
|
};
|
|
8232
8793
|
}
|
|
8233
|
-
|
|
8234
|
-
const
|
|
8235
|
-
if (
|
|
8794
|
+
resolveVectorConstructorCall(vectorType, args, scope, pos) {
|
|
8795
|
+
const expectedCount = vectorComponentCount(vectorType);
|
|
8796
|
+
if (args.length !== expectedCount) {
|
|
8236
8797
|
throw new CompileError(
|
|
8237
|
-
|
|
8798
|
+
`${AttributeType[vectorType]} takes ${expectedCount} numeric arguments, got ${args.length}`,
|
|
8238
8799
|
pos
|
|
8239
8800
|
);
|
|
8240
8801
|
}
|
|
8241
|
-
const
|
|
8242
|
-
|
|
8243
|
-
|
|
8244
|
-
|
|
8245
|
-
|
|
8246
|
-
|
|
8802
|
+
const scalarType = vectorComponentType(vectorType);
|
|
8803
|
+
const expectedType = { type: scalarType, required: true };
|
|
8804
|
+
const resolved = args.map((arg) => this.resolveExpr(arg, scope));
|
|
8805
|
+
for (const arg of resolved) {
|
|
8806
|
+
if (!isAssignable(arg.type, expectedType, this.ctx.types)) {
|
|
8807
|
+
throw new CompileError(
|
|
8808
|
+
`${AttributeType[vectorType]} component must be ${describeType(expectedType)}; got ${describeType(arg.type)}`,
|
|
8809
|
+
pos
|
|
8810
|
+
);
|
|
8811
|
+
}
|
|
8812
|
+
}
|
|
8813
|
+
return {
|
|
8814
|
+
pointer: {
|
|
8815
|
+
type: "function" /* function */,
|
|
8816
|
+
function: {
|
|
8817
|
+
type: "vectorConstructor" /* vectorConstructor */,
|
|
8818
|
+
info: {
|
|
8819
|
+
vectorType,
|
|
8820
|
+
componentPointers: resolved.map((arg) => arg.pointer)
|
|
8821
|
+
}
|
|
8822
|
+
}
|
|
8823
|
+
},
|
|
8824
|
+
type: { type: vectorType, required: true }
|
|
8825
|
+
};
|
|
8826
|
+
}
|
|
8827
|
+
resolveCollectionMutationStatement(expr, scope, pos) {
|
|
8828
|
+
const callee = expr.callee;
|
|
8829
|
+
if (callee.kind !== "member") {
|
|
8830
|
+
throw new CompileError(
|
|
8831
|
+
"Collection mutation calls must use member-call form.",
|
|
8832
|
+
pos
|
|
8833
|
+
);
|
|
8834
|
+
}
|
|
8835
|
+
const receiver = this.resolveExpr(callee.receiver, scope);
|
|
8836
|
+
if (receiver.type.type !== 6 /* List */ && receiver.type.type !== 5 /* Dictionary */ && receiver.type.type !== 9 /* Lookup */) {
|
|
8837
|
+
throw new CompileError(
|
|
8838
|
+
`Cannot call ${callee.name} on ${describeType(receiver.type)}; expected list, dictionary, or lookup set.`,
|
|
8839
|
+
pos
|
|
8840
|
+
);
|
|
8247
8841
|
}
|
|
8248
8842
|
const localMutation = localCollectionMutationTargetEntry(
|
|
8249
8843
|
callee.receiver,
|
|
@@ -8618,6 +9212,18 @@ var init_resolver = __esm({
|
|
|
8618
9212
|
required: t.required
|
|
8619
9213
|
};
|
|
8620
9214
|
}
|
|
9215
|
+
if (t.name === "Vector2") {
|
|
9216
|
+
return { type: 14 /* Vector2 */, required: t.required };
|
|
9217
|
+
}
|
|
9218
|
+
if (t.name === "Vector2Int") {
|
|
9219
|
+
return { type: 15 /* Vector2Int */, required: t.required };
|
|
9220
|
+
}
|
|
9221
|
+
if (t.name === "Vector3") {
|
|
9222
|
+
return { type: 16 /* Vector3 */, required: t.required };
|
|
9223
|
+
}
|
|
9224
|
+
if (t.name === "Vector3Int") {
|
|
9225
|
+
return { type: 17 /* Vector3Int */, required: t.required };
|
|
9226
|
+
}
|
|
8621
9227
|
const customType = this.ctx.types.find((ct) => ct.name === t.name);
|
|
8622
9228
|
if (customType) {
|
|
8623
9229
|
return {
|
|
@@ -8682,6 +9288,14 @@ var init_resolver = __esm({
|
|
|
8682
9288
|
return { type: 11 /* Sprite */, required };
|
|
8683
9289
|
case 12 /* Audio */:
|
|
8684
9290
|
return { type: 12 /* Audio */, required };
|
|
9291
|
+
case 14 /* Vector2 */:
|
|
9292
|
+
return { type: 14 /* Vector2 */, required };
|
|
9293
|
+
case 15 /* Vector2Int */:
|
|
9294
|
+
return { type: 15 /* Vector2Int */, required };
|
|
9295
|
+
case 16 /* Vector3 */:
|
|
9296
|
+
return { type: 16 /* Vector3 */, required };
|
|
9297
|
+
case 17 /* Vector3Int */:
|
|
9298
|
+
return { type: 17 /* Vector3Int */, required };
|
|
8685
9299
|
case 7 /* Custom */:
|
|
8686
9300
|
if (!isAttributeCustomBase(attr)) {
|
|
8687
9301
|
throw new CompileError(
|
|
@@ -9022,6 +9636,16 @@ ${status.parseErrors.map((error) => ` ${error.message}`).join("\n")}`
|
|
|
9022
9636
|
action: compileMigrationAction(status, change.nextData)
|
|
9023
9637
|
};
|
|
9024
9638
|
}
|
|
9639
|
+
if (change.recordKind === "attribute" && change.nextData.type === 10 && typeof change.nextData.code === "string" && change.nextData.code.trim().length > 0) {
|
|
9640
|
+
change.nextData = {
|
|
9641
|
+
...change.nextData,
|
|
9642
|
+
getter: await compileAttributeGetter(
|
|
9643
|
+
workspace,
|
|
9644
|
+
status,
|
|
9645
|
+
change.nextData
|
|
9646
|
+
)
|
|
9647
|
+
};
|
|
9648
|
+
}
|
|
9025
9649
|
if (change.kind === "create") {
|
|
9026
9650
|
change.nextData = {
|
|
9027
9651
|
...change.nextData,
|
|
@@ -9231,6 +9855,35 @@ function rewriteFilesFromState(workspace) {
|
|
|
9231
9855
|
workspace.state.records[key] = nextState;
|
|
9232
9856
|
}
|
|
9233
9857
|
}
|
|
9858
|
+
async function compileAttributeGetter(workspace, status, attributeData) {
|
|
9859
|
+
const types = [];
|
|
9860
|
+
const attributes = [];
|
|
9861
|
+
const enums = [];
|
|
9862
|
+
for (const record of status.reconstructed.values()) {
|
|
9863
|
+
if (record.recordKind === "type") types.push(record.fullData);
|
|
9864
|
+
else if (record.recordKind === "attribute")
|
|
9865
|
+
attributes.push(record.fullData);
|
|
9866
|
+
else if (record.recordKind === "enum") enums.push(record.fullData);
|
|
9867
|
+
}
|
|
9868
|
+
const attributeId = attributeData.id;
|
|
9869
|
+
const thisType = types.find((type) => {
|
|
9870
|
+
const schema = type.schema;
|
|
9871
|
+
return typeof schema === "object" && schema !== null && Object.values(schema).includes(attributeId);
|
|
9872
|
+
}) ?? null;
|
|
9873
|
+
const { raw } = await fetchProjectDocument(workspace);
|
|
9874
|
+
if (typeof raw.project !== "object" || raw.project === null) {
|
|
9875
|
+
throw new Error("Cannot compile getter: project record is unavailable.");
|
|
9876
|
+
}
|
|
9877
|
+
return compileNSGetter(String(attributeData.code), {
|
|
9878
|
+
project: raw.project,
|
|
9879
|
+
attributes,
|
|
9880
|
+
types,
|
|
9881
|
+
enums,
|
|
9882
|
+
thisType,
|
|
9883
|
+
returnTypeInfo: attributeData.returnTypeInfo,
|
|
9884
|
+
dialogueContext: null
|
|
9885
|
+
});
|
|
9886
|
+
}
|
|
9234
9887
|
function compileMigrationAction(status, migrationData) {
|
|
9235
9888
|
const types = [];
|
|
9236
9889
|
const attributes = [];
|
|
@@ -9257,6 +9910,7 @@ var init_push = __esm({
|
|
|
9257
9910
|
"src/commands/push.ts"() {
|
|
9258
9911
|
"use strict";
|
|
9259
9912
|
init_neoscript2();
|
|
9913
|
+
init_document();
|
|
9260
9914
|
init_convex();
|
|
9261
9915
|
init_ui();
|
|
9262
9916
|
init_project_version_intents();
|
|
@@ -9638,6 +10292,10 @@ function numberArg(value, name) {
|
|
|
9638
10292
|
if (typeof value === "number" && Number.isInteger(value)) return value;
|
|
9639
10293
|
throw new NSGetterRuntimeError(`${name} must be an integer`);
|
|
9640
10294
|
}
|
|
10295
|
+
function finiteNumberArg(value, name) {
|
|
10296
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
10297
|
+
throw new NSGetterRuntimeError(`${name} must be a finite number`);
|
|
10298
|
+
}
|
|
9641
10299
|
function evalPointer(pointer, scope, ctx) {
|
|
9642
10300
|
switch (pointer.type) {
|
|
9643
10301
|
case "value" /* value */:
|
|
@@ -9900,6 +10558,14 @@ function runtimeTypeCheck(value, checkType, ctx) {
|
|
|
9900
10558
|
return isRuntimeSpriteValue(value);
|
|
9901
10559
|
case 12 /* Audio */:
|
|
9902
10560
|
return isRuntimeFileValue(value) && !isRuntimeSpriteValue(value);
|
|
10561
|
+
case 14 /* Vector2 */:
|
|
10562
|
+
return isVector2Value(value);
|
|
10563
|
+
case 15 /* Vector2Int */:
|
|
10564
|
+
return isVector2IntValue(value);
|
|
10565
|
+
case 16 /* Vector3 */:
|
|
10566
|
+
return isVector3Value(value);
|
|
10567
|
+
case 17 /* Vector3Int */:
|
|
10568
|
+
return isVector3IntValue(value);
|
|
9903
10569
|
case 6 /* List */:
|
|
9904
10570
|
case 9 /* Lookup */:
|
|
9905
10571
|
return Array.isArray(value);
|
|
@@ -9921,6 +10587,24 @@ function runtimeTypeCheck(value, checkType, ctx) {
|
|
|
9921
10587
|
return false;
|
|
9922
10588
|
}
|
|
9923
10589
|
}
|
|
10590
|
+
function evalVectorConstructor(vectorType, componentPointers, scope, ctx) {
|
|
10591
|
+
const values = componentPointers.map(
|
|
10592
|
+
(pointer) => finiteNumberArg(
|
|
10593
|
+
evalPointer(pointer, scope, ctx),
|
|
10594
|
+
`${AttributeType[vectorType]} component`
|
|
10595
|
+
)
|
|
10596
|
+
);
|
|
10597
|
+
const integer = vectorType === 15 /* Vector2Int */ || vectorType === 17 /* Vector3Int */;
|
|
10598
|
+
if (integer && values.some((value) => !Number.isInteger(value))) {
|
|
10599
|
+
throw new NSGetterRuntimeError(
|
|
10600
|
+
`${AttributeType[vectorType]} components must be integers`
|
|
10601
|
+
);
|
|
10602
|
+
}
|
|
10603
|
+
if (vectorType === 14 /* Vector2 */ || vectorType === 15 /* Vector2Int */) {
|
|
10604
|
+
return { x: values[0], y: values[1] };
|
|
10605
|
+
}
|
|
10606
|
+
return { x: values[0], y: values[1], z: values[2] };
|
|
10607
|
+
}
|
|
9924
10608
|
function isRuntimeFileValue(value) {
|
|
9925
10609
|
if (typeof value !== "object") return false;
|
|
9926
10610
|
if (value === null) return false;
|
|
@@ -10304,6 +10988,14 @@ function evalFunction(fn, scope, ctx) {
|
|
|
10304
10988
|
const pointer = evalPointer(fn.info.pointer, scope, ctx);
|
|
10305
10989
|
return dialogueMemoryVisitCount(pointer, ctx) > 0;
|
|
10306
10990
|
}
|
|
10991
|
+
case "vectorConstructor" /* vectorConstructor */: {
|
|
10992
|
+
return evalVectorConstructor(
|
|
10993
|
+
fn.info.vectorType,
|
|
10994
|
+
fn.info.componentPointers,
|
|
10995
|
+
scope,
|
|
10996
|
+
ctx
|
|
10997
|
+
);
|
|
10998
|
+
}
|
|
10307
10999
|
case "count" /* count */: {
|
|
10308
11000
|
const c = evalPointer(fn.info.collectionPointer, scope, ctx);
|
|
10309
11001
|
return collectionLength(c);
|
|
@@ -10645,13 +11337,34 @@ function parseReturns(returns, document) {
|
|
|
10645
11337
|
if (returns === null) {
|
|
10646
11338
|
return RETURN_SHORTHANDS.unknown;
|
|
10647
11339
|
}
|
|
10648
|
-
const
|
|
10649
|
-
if (
|
|
10650
|
-
|
|
10651
|
-
|
|
11340
|
+
const trimmed = returns.trim();
|
|
11341
|
+
if (trimmed.startsWith("{")) {
|
|
11342
|
+
return JSON.parse(trimmed);
|
|
11343
|
+
}
|
|
11344
|
+
return parseReturnExpression(trimmed, document);
|
|
11345
|
+
}
|
|
11346
|
+
function parseReturnExpression(expression, document) {
|
|
11347
|
+
if (expression.endsWith("[]")) {
|
|
11348
|
+
return {
|
|
11349
|
+
// AttributeType.List
|
|
11350
|
+
type: 6,
|
|
11351
|
+
required: true,
|
|
11352
|
+
entryTypeInfo: parseReturnExpression(
|
|
11353
|
+
expression.slice(0, -2).trim(),
|
|
11354
|
+
document
|
|
11355
|
+
)
|
|
11356
|
+
};
|
|
11357
|
+
}
|
|
11358
|
+
if (expression.endsWith("?")) {
|
|
11359
|
+
return {
|
|
11360
|
+
...parseReturnExpression(expression.slice(0, -1).trim(), document),
|
|
11361
|
+
required: false
|
|
11362
|
+
};
|
|
10652
11363
|
}
|
|
11364
|
+
const shorthand = RETURN_SHORTHANDS[expression];
|
|
11365
|
+
if (shorthand !== void 0) return shorthand;
|
|
10653
11366
|
const type = document.types.find(
|
|
10654
|
-
(entry) => entry.name ===
|
|
11367
|
+
(entry) => entry.name === expression || entry.id === expression
|
|
10655
11368
|
);
|
|
10656
11369
|
if (type !== void 0) {
|
|
10657
11370
|
return {
|
|
@@ -10661,7 +11374,7 @@ function parseReturns(returns, document) {
|
|
|
10661
11374
|
};
|
|
10662
11375
|
}
|
|
10663
11376
|
const enumRecord = document.enums.find(
|
|
10664
|
-
(entry) => entry.name ===
|
|
11377
|
+
(entry) => entry.name === expression || entry.id === expression
|
|
10665
11378
|
);
|
|
10666
11379
|
if (enumRecord !== void 0) {
|
|
10667
11380
|
return {
|
|
@@ -10671,7 +11384,7 @@ function parseReturns(returns, document) {
|
|
|
10671
11384
|
};
|
|
10672
11385
|
}
|
|
10673
11386
|
throw new Error(
|
|
10674
|
-
`--returns "${
|
|
11387
|
+
`--returns "${expression}" is not a shorthand (bool|int|string|float|unknown, with [] / ? suffixes), a known type/enum name, or TNSTypeInfo JSON.`
|
|
10675
11388
|
);
|
|
10676
11389
|
}
|
|
10677
11390
|
function resolveThisType(thisRef, document) {
|
|
@@ -10773,8 +11486,9 @@ async function runScript(workspace, command, options) {
|
|
|
10773
11486
|
line: error.line,
|
|
10774
11487
|
column: error.column
|
|
10775
11488
|
};
|
|
11489
|
+
const hint = options.returns === null && error.message.includes("not assignable to declared return type") ? '\n(eval defaults to an unknown return type \u2014 pass --returns, e.g. --returns "string[]")' : "";
|
|
10776
11490
|
console.log(
|
|
10777
|
-
options.json ? JSON.stringify(report2, null, 2) : `Compile error at ${error.line}:${error.column} \u2014 ${error.message}`
|
|
11491
|
+
options.json ? JSON.stringify(report2, null, 2) : `Compile error at ${error.line}:${error.column} \u2014 ${error.message}${hint}`
|
|
10778
11492
|
);
|
|
10779
11493
|
process.exitCode = 1;
|
|
10780
11494
|
return;
|
|
@@ -10787,6 +11501,10 @@ async function runScript(workspace, command, options) {
|
|
|
10787
11501
|
);
|
|
10788
11502
|
return;
|
|
10789
11503
|
}
|
|
11504
|
+
if (command === "compile") {
|
|
11505
|
+
console.log(JSON.stringify(compiled, null, 2));
|
|
11506
|
+
return;
|
|
11507
|
+
}
|
|
10790
11508
|
let evalValues = document.values;
|
|
10791
11509
|
if (options.save !== null) {
|
|
10792
11510
|
if (command === "apply") {
|
|
@@ -10814,7 +11532,7 @@ async function runScript(workspace, command, options) {
|
|
|
10814
11532
|
)
|
|
10815
11533
|
};
|
|
10816
11534
|
const overlaidDocument = { ...document, values: evalValues };
|
|
10817
|
-
const
|
|
11535
|
+
const evaluatorContext2 = {
|
|
10818
11536
|
vm,
|
|
10819
11537
|
thisValue: resolveThisValue(options.thisRef, overlaidDocument),
|
|
10820
11538
|
rootValue: buildRootValue(overlaidDocument),
|
|
@@ -10823,14 +11541,14 @@ async function runScript(workspace, command, options) {
|
|
|
10823
11541
|
if (command === "eval") {
|
|
10824
11542
|
const value = evaluateNSGetter(
|
|
10825
11543
|
compiled,
|
|
10826
|
-
|
|
11544
|
+
evaluatorContext2
|
|
10827
11545
|
);
|
|
10828
11546
|
console.log(
|
|
10829
11547
|
options.json ? JSON.stringify({ ok: true, value }, null, 2) : JSON.stringify(value, null, 2)
|
|
10830
11548
|
);
|
|
10831
11549
|
return;
|
|
10832
11550
|
}
|
|
10833
|
-
const result = evaluateNSAction(compiled,
|
|
11551
|
+
const result = evaluateNSAction(compiled, evaluatorContext2);
|
|
10834
11552
|
const report = { ok: true, writes: result.writes };
|
|
10835
11553
|
console.log(
|
|
10836
11554
|
options.json ? JSON.stringify(report, null, 2) : `${result.writes.length} write intent(s):
|
|
@@ -11811,15 +12529,18 @@ async function runBranch(workspace, subcommand, positional, fromRef) {
|
|
|
11811
12529
|
console.log(JSON.stringify(versions, null, 2));
|
|
11812
12530
|
return;
|
|
11813
12531
|
}
|
|
12532
|
+
const showArchived = process.argv.includes("--all");
|
|
11814
12533
|
for (const version of versions) {
|
|
11815
|
-
|
|
11816
|
-
|
|
11817
|
-
}
|
|
12534
|
+
const archived = version.archivedAt !== null && version.archivedAt !== void 0;
|
|
12535
|
+
if (archived && !showArchived) continue;
|
|
11818
12536
|
const current = version.id === workspace.config.versionId;
|
|
11819
12537
|
const marker = current ? color.green("*") : " ";
|
|
11820
12538
|
const kind = version.kind === "branch" ? `${sym.branch} branch ` : `${sym.tag} version`;
|
|
11821
12539
|
const label = current ? color.bold(displayLabel(version)) : displayLabel(version);
|
|
11822
|
-
|
|
12540
|
+
const suffix = archived ? ` ${color.dim("(archived)")}` : "";
|
|
12541
|
+
console.log(
|
|
12542
|
+
`${marker} ${kind} ${label} ${color.dim(version.id)}${suffix}`
|
|
12543
|
+
);
|
|
11823
12544
|
}
|
|
11824
12545
|
return;
|
|
11825
12546
|
}
|
|
@@ -12116,6 +12837,11 @@ async function runMerge(workspace, sourceRef, dryRun, runMigrations = false) {
|
|
|
12116
12837
|
success(
|
|
12117
12838
|
`Merged ${color.bold(String(merged))} record(s) from ${displayLabel(source)}.`
|
|
12118
12839
|
);
|
|
12840
|
+
if (result.sourceArchived === true) {
|
|
12841
|
+
console.log(
|
|
12842
|
+
`Archived ${displayLabel(source)} \u2014 restore it with "neo branch restore ${typeof source.name === "string" ? source.name : source.id}".`
|
|
12843
|
+
);
|
|
12844
|
+
}
|
|
12119
12845
|
if (merged > 0) {
|
|
12120
12846
|
console.log("Pulling merged state...");
|
|
12121
12847
|
await runPull(workspace, { force: false });
|
|
@@ -12274,141 +13000,1035 @@ var init_branch = __esm({
|
|
|
12274
13000
|
}
|
|
12275
13001
|
});
|
|
12276
13002
|
|
|
12277
|
-
// src/commands/
|
|
12278
|
-
var
|
|
12279
|
-
__export(
|
|
12280
|
-
|
|
12281
|
-
|
|
12282
|
-
runFiles: () => runFiles,
|
|
12283
|
-
runLoc: () => runLoc,
|
|
12284
|
-
runRecords: () => runRecords,
|
|
12285
|
-
runValues: () => runValues
|
|
13003
|
+
// src/commands/dialogue-spec.ts
|
|
13004
|
+
var dialogue_spec_exports = {};
|
|
13005
|
+
__export(dialogue_spec_exports, {
|
|
13006
|
+
runDialogueApply: () => runDialogueApply,
|
|
13007
|
+
runDialogueExport: () => runDialogueExport
|
|
12286
13008
|
});
|
|
13009
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
12287
13010
|
import { readFileSync as readFileSync9 } from "node:fs";
|
|
12288
|
-
function
|
|
12289
|
-
|
|
12290
|
-
|
|
12291
|
-
|
|
12292
|
-
|
|
12293
|
-
|
|
12294
|
-
|
|
12295
|
-
|
|
12296
|
-
|
|
12297
|
-
|
|
12298
|
-
|
|
12299
|
-
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
|
|
13011
|
+
async function runDialogueExport(workspace, ref) {
|
|
13012
|
+
const { raw } = await fetchProjectDocument(workspace);
|
|
13013
|
+
const dialogues = Array.isArray(raw.dialogues) ? raw.dialogues.filter(isObjectRecord2) : [];
|
|
13014
|
+
const dialogue = dialogues.find(
|
|
13015
|
+
(entry) => entry.id === ref || entry.name === ref
|
|
13016
|
+
);
|
|
13017
|
+
if (dialogue === void 0) {
|
|
13018
|
+
throw new Error(`No dialogue matches "${ref}".`);
|
|
13019
|
+
}
|
|
13020
|
+
const locById = /* @__PURE__ */ new Map();
|
|
13021
|
+
const texts = Array.isArray(raw.localizedTexts) ? raw.localizedTexts.filter(isObjectRecord2) : [];
|
|
13022
|
+
for (const text of texts) {
|
|
13023
|
+
const locales = isObjectRecord2(text.localeValues) ? text.localeValues : {};
|
|
13024
|
+
const main2 = isObjectRecord2(locales["en-US"]) ? locales["en-US"] : {};
|
|
13025
|
+
if (typeof main2.value === "string") {
|
|
13026
|
+
locById.set(String(text.id), main2.value);
|
|
13027
|
+
}
|
|
13028
|
+
}
|
|
13029
|
+
const nodes = isObjectRecord2(dialogue.nodes) ? dialogue.nodes : {};
|
|
13030
|
+
const keyById = /* @__PURE__ */ new Map();
|
|
13031
|
+
const used = /* @__PURE__ */ new Set();
|
|
13032
|
+
for (const [nodeId, node] of Object.entries(nodes)) {
|
|
13033
|
+
let key = typeof node.name === "string" && node.name.length > 0 ? node.name : nodeId.slice(0, 8);
|
|
13034
|
+
while (used.has(key)) key = `${key}_`;
|
|
13035
|
+
used.add(key);
|
|
13036
|
+
keyById.set(nodeId, key);
|
|
13037
|
+
}
|
|
13038
|
+
const refKey = (nodeId) => typeof nodeId === "string" ? keyById.get(nodeId) ?? null : null;
|
|
13039
|
+
const specNodes = {};
|
|
13040
|
+
for (const [nodeId, node] of Object.entries(nodes)) {
|
|
13041
|
+
const spec = {};
|
|
13042
|
+
if (typeof node.text === "string") {
|
|
13043
|
+
spec.say = locById.get(node.text) ?? "";
|
|
13044
|
+
}
|
|
13045
|
+
const optionSettings = isObjectRecord2(node.optionSettings) ? node.optionSettings : null;
|
|
13046
|
+
const options = Array.isArray(optionSettings?.options) ? optionSettings.options.filter(isObjectRecord2) : [];
|
|
13047
|
+
if (options.length > 0) {
|
|
13048
|
+
spec.options = options.map((option) => ({
|
|
13049
|
+
label: option.name ?? "",
|
|
13050
|
+
text: typeof option.text === "string" ? locById.get(option.text) ?? "" : "",
|
|
13051
|
+
to: refKey(option.toNodeId)
|
|
13052
|
+
}));
|
|
13053
|
+
}
|
|
13054
|
+
const actions = Array.isArray(node.actions) ? node.actions.filter(isObjectRecord2) : [];
|
|
13055
|
+
if (actions.length > 0 || node.type === 2) {
|
|
13056
|
+
spec.do = actions.map((action) => {
|
|
13057
|
+
const logic = isObjectRecord2(action.logic) ? action.logic : {};
|
|
13058
|
+
const bind = typeof node.primaryLinkedValueId === "string" ? node.primaryLinkedValueId : void 0;
|
|
13059
|
+
if (typeof logic.code === "string") {
|
|
13060
|
+
return { code: logic.code, bind };
|
|
13061
|
+
}
|
|
13062
|
+
return { ir: logic.action ?? null, bind };
|
|
13063
|
+
});
|
|
13064
|
+
}
|
|
13065
|
+
if (Array.isArray(node.outcomes)) spec.outcomes = node.outcomes;
|
|
13066
|
+
if (typeof node.toNodeId === "string") spec.to = refKey(node.toNodeId);
|
|
13067
|
+
specNodes[keyById.get(nodeId) ?? nodeId] = spec;
|
|
12303
13068
|
}
|
|
12304
|
-
|
|
13069
|
+
const trigger = isObjectRecord2(dialogue.triggerNode) ? dialogue.triggerNode : null;
|
|
13070
|
+
const groupSettings = trigger !== null && isObjectRecord2(trigger.dialogueGroupSettings) ? trigger.dialogueGroupSettings : null;
|
|
13071
|
+
const priority = groupSettings !== null && isObjectRecord2(groupSettings.priority) ? groupSettings.priority : null;
|
|
13072
|
+
const occurrence = trigger !== null && isObjectRecord2(trigger.occurrenceLimitSettings) ? trigger.occurrenceLimitSettings : null;
|
|
13073
|
+
console.log(
|
|
13074
|
+
JSON.stringify(
|
|
13075
|
+
{
|
|
13076
|
+
id: dialogue.id,
|
|
13077
|
+
name: dialogue.name,
|
|
13078
|
+
description: dialogue.description ?? null,
|
|
13079
|
+
linkedValueId: dialogue.primaryLinkedValueId ?? null,
|
|
13080
|
+
group: groupSettings === null ? null : {
|
|
13081
|
+
id: groupSettings.dialogueGroupId,
|
|
13082
|
+
priority: priority?.priorityTypeId ?? null,
|
|
13083
|
+
relativeOrder: priority?.relativeOrder ?? 0,
|
|
13084
|
+
occurrenceLimit: occurrence?.count ?? null
|
|
13085
|
+
},
|
|
13086
|
+
when: (() => {
|
|
13087
|
+
const conditions = Array.isArray(trigger?.conditions) ? trigger.conditions.filter(isObjectRecord2) : [];
|
|
13088
|
+
const first = conditions[0];
|
|
13089
|
+
return first !== void 0 && typeof first.code === "string" ? first.code : null;
|
|
13090
|
+
})(),
|
|
13091
|
+
start: refKey(trigger?.toNodeId),
|
|
13092
|
+
nodes: specNodes
|
|
13093
|
+
},
|
|
13094
|
+
null,
|
|
13095
|
+
2
|
|
13096
|
+
)
|
|
13097
|
+
);
|
|
12305
13098
|
}
|
|
12306
|
-
async function
|
|
12307
|
-
const
|
|
12308
|
-
if (
|
|
12309
|
-
|
|
12310
|
-
|
|
12311
|
-
|
|
13099
|
+
async function runDialogueApply(workspace, file, dryRun) {
|
|
13100
|
+
const spec = JSON.parse(readFileSync9(file, "utf8"));
|
|
13101
|
+
if (!isObjectRecord2(spec)) throw new Error("Spec must be a JSON object.");
|
|
13102
|
+
if (typeof spec.name !== "string") throw new Error('Spec needs a "name".');
|
|
13103
|
+
if (!isObjectRecord2(spec.nodes)) throw new Error('Spec needs "nodes".');
|
|
13104
|
+
if (typeof spec.start !== "string") throw new Error('Spec needs "start".');
|
|
13105
|
+
const linkedValueId = typeof spec.linkedValueId === "string" ? spec.linkedValueId : null;
|
|
13106
|
+
if (linkedValueId === null) {
|
|
13107
|
+
throw new Error('Spec needs "linkedValueId" (the bound value id).');
|
|
13108
|
+
}
|
|
13109
|
+
const client = NeoApiClient.forBaseUrl(workspace.config.apiBaseUrl);
|
|
13110
|
+
const base = `/api/projects/${workspace.config.projectId}/versions/${workspace.config.versionId}`;
|
|
13111
|
+
const ids = {};
|
|
13112
|
+
for (const key of Object.keys(spec.nodes)) ids[key] = randomUUID4();
|
|
13113
|
+
if (ids[spec.start] === void 0) {
|
|
13114
|
+
throw new Error(`"start" references unknown node "${spec.start}".`);
|
|
13115
|
+
}
|
|
13116
|
+
const pendingTexts = [];
|
|
13117
|
+
const nodes = {};
|
|
13118
|
+
let index = 0;
|
|
13119
|
+
for (const [key, rawNode] of Object.entries(spec.nodes)) {
|
|
13120
|
+
if (!isObjectRecord2(rawNode)) {
|
|
13121
|
+
throw new Error(`Node "${key}" must be an object.`);
|
|
13122
|
+
}
|
|
13123
|
+
const nodeId = ids[key];
|
|
13124
|
+
const layout = {
|
|
13125
|
+
x: index % 4 * 380,
|
|
13126
|
+
y: 220 + Math.floor(index / 4) * 240,
|
|
13127
|
+
width: 320,
|
|
13128
|
+
height: 160,
|
|
13129
|
+
isOpen: true
|
|
13130
|
+
};
|
|
13131
|
+
index += 1;
|
|
13132
|
+
const resolveTo = (target) => {
|
|
13133
|
+
if (target === null || target === void 0) return void 0;
|
|
13134
|
+
const id = ids[String(target)];
|
|
13135
|
+
if (id === void 0) {
|
|
13136
|
+
throw new Error(
|
|
13137
|
+
`Node "${key}" points at unknown node "${String(target)}".`
|
|
13138
|
+
);
|
|
13139
|
+
}
|
|
13140
|
+
return id;
|
|
13141
|
+
};
|
|
13142
|
+
if (Array.isArray(rawNode.do)) {
|
|
13143
|
+
const groups = [];
|
|
13144
|
+
for (const step of rawNode.do) {
|
|
13145
|
+
if (!isObjectRecord2(step)) {
|
|
13146
|
+
throw new Error(`Node "${key}" has a non-object "do" step.`);
|
|
13147
|
+
}
|
|
13148
|
+
const bind = typeof step.bind === "string" ? step.bind : linkedValueId;
|
|
13149
|
+
const last = groups[groups.length - 1];
|
|
13150
|
+
if (last !== void 0 && last.bind === bind) {
|
|
13151
|
+
last.steps.push(step);
|
|
13152
|
+
} else {
|
|
13153
|
+
groups.push({ bind, steps: [step] });
|
|
13154
|
+
}
|
|
13155
|
+
}
|
|
13156
|
+
let chainNodeId = nodeId;
|
|
13157
|
+
for (const [index2, group2] of groups.entries()) {
|
|
13158
|
+
const actions = [];
|
|
13159
|
+
for (const step of group2.steps) {
|
|
13160
|
+
let ir = step.ir;
|
|
13161
|
+
if (typeof step.code === "string") {
|
|
13162
|
+
const compiled = await client.post(
|
|
13163
|
+
`${base}/dialogue-logic/compile`,
|
|
13164
|
+
{
|
|
13165
|
+
code: step.code,
|
|
13166
|
+
target: 1,
|
|
13167
|
+
dialogue: {},
|
|
13168
|
+
node: { primaryLinkedValueId: group2.bind }
|
|
13169
|
+
}
|
|
13170
|
+
);
|
|
13171
|
+
ir = compiled.action;
|
|
13172
|
+
}
|
|
13173
|
+
if (!isObjectRecord2(ir)) {
|
|
13174
|
+
throw new Error(`Node "${key}" "do" step needs "code" or "ir".`);
|
|
13175
|
+
}
|
|
13176
|
+
actions.push({
|
|
13177
|
+
id: randomUUID4(),
|
|
13178
|
+
type: 0,
|
|
13179
|
+
logic: typeof step.code === "string" ? { type: 1, code: step.code, action: ir } : { type: 0, action: ir }
|
|
13180
|
+
});
|
|
13181
|
+
}
|
|
13182
|
+
const isLast = index2 === groups.length - 1;
|
|
13183
|
+
const nextChainId = isLast ? void 0 : randomUUID4();
|
|
13184
|
+
nodes[chainNodeId] = {
|
|
13185
|
+
id: chainNodeId,
|
|
13186
|
+
type: 2,
|
|
13187
|
+
name: index2 === 0 ? key : `${key} (${index2 + 1})`,
|
|
13188
|
+
layout,
|
|
13189
|
+
linkedValues: [.../* @__PURE__ */ new Set([linkedValueId, group2.bind])].map(
|
|
13190
|
+
(valueId) => ({ source: 1, valueId })
|
|
13191
|
+
),
|
|
13192
|
+
primaryLinkedValueId: group2.bind,
|
|
13193
|
+
actions,
|
|
13194
|
+
...isLast ? resolveTo(rawNode.to) !== void 0 ? { toNodeId: resolveTo(rawNode.to) } : {} : { toNodeId: nextChainId }
|
|
13195
|
+
};
|
|
13196
|
+
if (nextChainId !== void 0) chainNodeId = nextChainId;
|
|
13197
|
+
}
|
|
13198
|
+
continue;
|
|
12312
13199
|
}
|
|
12313
|
-
|
|
12314
|
-
|
|
12315
|
-
console.log(JSON.stringify(fromMap, null, 2));
|
|
12316
|
-
return;
|
|
13200
|
+
if (typeof rawNode.say !== "string") {
|
|
13201
|
+
throw new Error(`Node "${key}" needs "say" (text) or "do" (actions).`);
|
|
12317
13202
|
}
|
|
12318
|
-
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
|
|
12322
|
-
|
|
12323
|
-
|
|
13203
|
+
const textId = randomUUID4();
|
|
13204
|
+
pendingTexts.push({
|
|
13205
|
+
textId,
|
|
13206
|
+
value: rawNode.say,
|
|
13207
|
+
nodeId,
|
|
13208
|
+
choiceId: null
|
|
13209
|
+
});
|
|
13210
|
+
const node = {
|
|
13211
|
+
id: nodeId,
|
|
13212
|
+
type: 1,
|
|
13213
|
+
name: key,
|
|
13214
|
+
layout,
|
|
13215
|
+
linkedValues: [{ source: 1, valueId: linkedValueId }],
|
|
13216
|
+
primaryLinkedValueId: linkedValueId,
|
|
13217
|
+
text: textId,
|
|
13218
|
+
variables: null
|
|
13219
|
+
};
|
|
13220
|
+
if (Array.isArray(rawNode.options) && rawNode.options.length > 0) {
|
|
13221
|
+
const options = [];
|
|
13222
|
+
for (const rawOption of rawNode.options) {
|
|
13223
|
+
if (!isObjectRecord2(rawOption)) {
|
|
13224
|
+
throw new Error(`Node "${key}" has a non-object option.`);
|
|
13225
|
+
}
|
|
13226
|
+
const option = rawOption;
|
|
13227
|
+
const optionId = randomUUID4();
|
|
13228
|
+
const optionTextId = randomUUID4();
|
|
13229
|
+
let optionSettings = null;
|
|
13230
|
+
if (typeof option.if === "string") {
|
|
13231
|
+
const compiledIf = await client.post(
|
|
13232
|
+
`${base}/dialogue-logic/compile`,
|
|
13233
|
+
{
|
|
13234
|
+
code: option.if,
|
|
13235
|
+
target: 0,
|
|
13236
|
+
dialogue: { primaryLinkedValueId: linkedValueId },
|
|
13237
|
+
node: { primaryLinkedValueId: linkedValueId }
|
|
13238
|
+
}
|
|
13239
|
+
);
|
|
13240
|
+
optionSettings = {
|
|
13241
|
+
selectableConditions: [
|
|
13242
|
+
{ type: 1, code: option.if, getter: compiledIf.getter }
|
|
13243
|
+
]
|
|
13244
|
+
};
|
|
12324
13245
|
}
|
|
13246
|
+
options.push({
|
|
13247
|
+
id: optionId,
|
|
13248
|
+
name: option.label,
|
|
13249
|
+
text: optionTextId,
|
|
13250
|
+
toNodeId: resolveTo(option.to),
|
|
13251
|
+
variables: null,
|
|
13252
|
+
...optionSettings === null ? {} : { settings: optionSettings }
|
|
13253
|
+
});
|
|
13254
|
+
pendingTexts.push({
|
|
13255
|
+
textId: optionTextId,
|
|
13256
|
+
value: option.text,
|
|
13257
|
+
nodeId,
|
|
13258
|
+
choiceId: optionId
|
|
13259
|
+
});
|
|
12325
13260
|
}
|
|
13261
|
+
node.optionSettings = { options };
|
|
13262
|
+
} else if (resolveTo(rawNode.to) !== void 0) {
|
|
13263
|
+
node.toNodeId = resolveTo(rawNode.to);
|
|
12326
13264
|
}
|
|
12327
|
-
|
|
13265
|
+
nodes[nodeId] = node;
|
|
12328
13266
|
}
|
|
12329
|
-
|
|
12330
|
-
|
|
12331
|
-
|
|
12332
|
-
|
|
12333
|
-
|
|
12334
|
-
|
|
12335
|
-
|
|
12336
|
-
|
|
13267
|
+
const group = isObjectRecord2(spec.group) ? spec.group : null;
|
|
13268
|
+
const triggerConditions = [];
|
|
13269
|
+
if (typeof spec.when === "string") {
|
|
13270
|
+
const compiled = await client.post(
|
|
13271
|
+
`${base}/dialogue-logic/compile`,
|
|
13272
|
+
{
|
|
13273
|
+
code: spec.when,
|
|
13274
|
+
target: 0,
|
|
13275
|
+
dialogue: {
|
|
13276
|
+
primaryLinkedValueId: linkedValueId,
|
|
13277
|
+
triggerNode: {
|
|
13278
|
+
primaryLinkedValueId: linkedValueId,
|
|
13279
|
+
dialogueGroupSettings: group === null ? null : { lookupValueId: linkedValueId }
|
|
13280
|
+
}
|
|
13281
|
+
}
|
|
13282
|
+
}
|
|
13283
|
+
);
|
|
13284
|
+
triggerConditions.push({
|
|
13285
|
+
type: 1,
|
|
13286
|
+
code: spec.when,
|
|
13287
|
+
getter: compiled.getter
|
|
13288
|
+
});
|
|
12337
13289
|
}
|
|
12338
|
-
|
|
12339
|
-
|
|
12340
|
-
|
|
12341
|
-
|
|
12342
|
-
|
|
12343
|
-
|
|
12344
|
-
|
|
12345
|
-
|
|
12346
|
-
|
|
12347
|
-
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
}
|
|
12354
|
-
|
|
12355
|
-
|
|
13290
|
+
const trigger = {
|
|
13291
|
+
id: randomUUID4(),
|
|
13292
|
+
type: 0,
|
|
13293
|
+
name: null,
|
|
13294
|
+
layout: { x: 0, y: 0, width: 320, height: 160, isOpen: true },
|
|
13295
|
+
linkedValues: [{ source: 3, valueId: linkedValueId }],
|
|
13296
|
+
primaryLinkedValueId: linkedValueId,
|
|
13297
|
+
conditions: triggerConditions,
|
|
13298
|
+
dialogueGroupSettings: group === null ? null : {
|
|
13299
|
+
dialogueGroupId: group.id,
|
|
13300
|
+
lookupValueId: linkedValueId,
|
|
13301
|
+
priority: {
|
|
13302
|
+
priorityTypeId: group.priority,
|
|
13303
|
+
relativeOrder: typeof group.relativeOrder === "number" ? group.relativeOrder : 0
|
|
13304
|
+
}
|
|
13305
|
+
},
|
|
13306
|
+
occurrenceLimitSettings: group !== null && typeof group.occurrenceLimit === "number" ? { count: group.occurrenceLimit } : null,
|
|
13307
|
+
toNodeId: ids[spec.start]
|
|
13308
|
+
};
|
|
13309
|
+
const body = {
|
|
13310
|
+
name: spec.name,
|
|
13311
|
+
description: typeof spec.description === "string" ? spec.description : null,
|
|
13312
|
+
nodes,
|
|
13313
|
+
linkedValues: [{ source: 3, valueId: linkedValueId }],
|
|
13314
|
+
primaryLinkedValueId: linkedValueId,
|
|
13315
|
+
settings: { defaultSaveOptionChoices: true },
|
|
13316
|
+
triggerNode: trigger
|
|
13317
|
+
};
|
|
13318
|
+
if (dryRun) {
|
|
12356
13319
|
console.log(
|
|
12357
|
-
|
|
12358
|
-
filtered.map((entry) => ({
|
|
12359
|
-
id: entry.id,
|
|
12360
|
-
attributeId: entry.attributeId,
|
|
12361
|
-
value: entry.value
|
|
12362
|
-
})),
|
|
12363
|
-
null,
|
|
12364
|
-
2
|
|
12365
|
-
)
|
|
13320
|
+
`Would create "${spec.name}": ${Object.keys(nodes).length} node(s), ${pendingTexts.length} string(s).`
|
|
12366
13321
|
);
|
|
12367
13322
|
return;
|
|
12368
13323
|
}
|
|
12369
|
-
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
|
|
12375
|
-
|
|
12376
|
-
|
|
12377
|
-
|
|
12378
|
-
|
|
12379
|
-
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
|
|
12400
|
-
|
|
12401
|
-
|
|
12402
|
-
|
|
12403
|
-
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
13324
|
+
const created = await client.post(
|
|
13325
|
+
`${base}/dialogues/create`,
|
|
13326
|
+
body
|
|
13327
|
+
);
|
|
13328
|
+
const dialogue = isObjectRecord2(created.dialogue) ? created.dialogue : created;
|
|
13329
|
+
const dialogueId = String(dialogue.id);
|
|
13330
|
+
const convex = await createConvexClient(workspace);
|
|
13331
|
+
const now = Date.now();
|
|
13332
|
+
await convex.mutation(api.serverProjectRecordOperations.commitFromSession, {
|
|
13333
|
+
projectId: workspace.config.projectId,
|
|
13334
|
+
versionId: workspace.config.versionId,
|
|
13335
|
+
operation: "create",
|
|
13336
|
+
changes: pendingTexts.map((text) => ({
|
|
13337
|
+
recordKind: "localized-text",
|
|
13338
|
+
recordId: text.textId,
|
|
13339
|
+
operation: "create",
|
|
13340
|
+
nextData: {
|
|
13341
|
+
id: text.textId,
|
|
13342
|
+
projectId: workspace.config.projectId,
|
|
13343
|
+
sourceComment: null,
|
|
13344
|
+
links: [
|
|
13345
|
+
text.choiceId === null ? {
|
|
13346
|
+
kind: "dialogue-node-text",
|
|
13347
|
+
recordKind: "dialogue-node",
|
|
13348
|
+
recordId: text.nodeId,
|
|
13349
|
+
nodeId: text.nodeId,
|
|
13350
|
+
fieldPath: "text"
|
|
13351
|
+
} : {
|
|
13352
|
+
kind: "dialogue-choice-text",
|
|
13353
|
+
recordKind: "dialogue-node",
|
|
13354
|
+
recordId: text.nodeId,
|
|
13355
|
+
nodeId: text.nodeId,
|
|
13356
|
+
choiceId: text.choiceId,
|
|
13357
|
+
fieldPath: `optionSettings.options.${text.choiceId}.text`
|
|
13358
|
+
}
|
|
13359
|
+
],
|
|
13360
|
+
localeValues: {
|
|
13361
|
+
"en-US": {
|
|
13362
|
+
value: text.value,
|
|
13363
|
+
statusId: "localization-status-approved",
|
|
13364
|
+
localeComment: null,
|
|
13365
|
+
updatedAt: now
|
|
13366
|
+
}
|
|
13367
|
+
},
|
|
13368
|
+
createdAt: now,
|
|
13369
|
+
updatedAt: now
|
|
13370
|
+
},
|
|
13371
|
+
intent: createProjectVersionIntent("localized-text.create", {
|
|
13372
|
+
source: "neo-cli-dialogue-apply"
|
|
13373
|
+
}),
|
|
13374
|
+
expectedBaseContentHash: null
|
|
13375
|
+
})),
|
|
13376
|
+
summary: `neo dialogue apply: ${spec.name}`
|
|
13377
|
+
});
|
|
13378
|
+
success(
|
|
13379
|
+
`Created "${spec.name}" (${dialogueId}) \u2014 ${Object.keys(nodes).length} node(s), ${pendingTexts.length} string(s).`
|
|
13380
|
+
);
|
|
13381
|
+
console.log(`${sym.arrow} validate it: neo dialogue dryrun ${dialogueId}`);
|
|
13382
|
+
}
|
|
13383
|
+
var init_dialogue_spec = __esm({
|
|
13384
|
+
"src/commands/dialogue-spec.ts"() {
|
|
13385
|
+
"use strict";
|
|
13386
|
+
init_document();
|
|
13387
|
+
init_projection();
|
|
13388
|
+
init_convex();
|
|
13389
|
+
init_project_version_intents();
|
|
13390
|
+
init_http();
|
|
13391
|
+
init_ui();
|
|
13392
|
+
}
|
|
13393
|
+
});
|
|
13394
|
+
|
|
13395
|
+
// src/commands/dialogue-dryrun.ts
|
|
13396
|
+
var dialogue_dryrun_exports = {};
|
|
13397
|
+
__export(dialogue_dryrun_exports, {
|
|
13398
|
+
runDialogueDryrun: () => runDialogueDryrun
|
|
13399
|
+
});
|
|
13400
|
+
async function runDialogueDryrun(workspace, ref, json) {
|
|
13401
|
+
const { raw } = await fetchProjectDocument(workspace);
|
|
13402
|
+
const document = readDocumentArrays(raw);
|
|
13403
|
+
const world = buildWorld(document, raw);
|
|
13404
|
+
const dialogues = (Array.isArray(raw.dialogues) ? raw.dialogues.filter(isObjectRecord2) : []).filter(
|
|
13405
|
+
(dialogue) => ref === null || dialogue.id === ref || dialogue.name === ref
|
|
13406
|
+
);
|
|
13407
|
+
if (dialogues.length === 0) {
|
|
13408
|
+
throw new Error(
|
|
13409
|
+
ref === null ? "The project has no dialogues." : `No dialogue matches "${ref}".`
|
|
13410
|
+
);
|
|
13411
|
+
}
|
|
13412
|
+
const reports = dialogues.map((dialogue) => dryrunDialogue(dialogue, world));
|
|
13413
|
+
if (json) {
|
|
13414
|
+
console.log(JSON.stringify(reports, null, 2));
|
|
13415
|
+
} else {
|
|
13416
|
+
for (const report of reports) printReport(report);
|
|
13417
|
+
const failed = reports.filter((report) => report.violations.length > 0);
|
|
13418
|
+
console.log("");
|
|
13419
|
+
if (failed.length === 0) {
|
|
13420
|
+
console.log(
|
|
13421
|
+
`${sym.ok} ${reports.length} dialogue(s) dry-ran clean from a fresh save.`
|
|
13422
|
+
);
|
|
13423
|
+
} else {
|
|
13424
|
+
console.log(
|
|
13425
|
+
`${sym.fail} ${failed.length}/${reports.length} dialogue(s) have violations.`
|
|
13426
|
+
);
|
|
13427
|
+
}
|
|
13428
|
+
}
|
|
13429
|
+
if (reports.some((report) => report.violations.length > 0)) {
|
|
13430
|
+
process.exitCode = 1;
|
|
13431
|
+
}
|
|
13432
|
+
}
|
|
13433
|
+
function buildWorld(document, raw) {
|
|
13434
|
+
const valueById = new Map(
|
|
13435
|
+
document.values.map((value) => [String(value.id), value])
|
|
13436
|
+
);
|
|
13437
|
+
const attributeById = new Map(
|
|
13438
|
+
document.attributes.map((attribute) => [String(attribute.id), attribute])
|
|
13439
|
+
);
|
|
13440
|
+
const reachable = (rootContainerId) => {
|
|
13441
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13442
|
+
const queue = rootContainerId === null ? [] : [rootContainerId];
|
|
13443
|
+
while (queue.length > 0) {
|
|
13444
|
+
const id = queue.pop();
|
|
13445
|
+
if (id === void 0 || seen.has(id)) continue;
|
|
13446
|
+
const record = valueById.get(id);
|
|
13447
|
+
if (record === void 0) continue;
|
|
13448
|
+
seen.add(id);
|
|
13449
|
+
const value = record.value;
|
|
13450
|
+
const children = Array.isArray(value) ? value : isObjectRecord2(value) ? Object.values(value) : [];
|
|
13451
|
+
for (const child of children) {
|
|
13452
|
+
if (typeof child === "string" && valueById.has(child)) {
|
|
13453
|
+
queue.push(child);
|
|
13454
|
+
}
|
|
13455
|
+
}
|
|
13456
|
+
}
|
|
13457
|
+
return seen;
|
|
13458
|
+
};
|
|
13459
|
+
let saveOwned = /* @__PURE__ */ new Set();
|
|
13460
|
+
let sessionOwned = /* @__PURE__ */ new Set();
|
|
13461
|
+
for (const { root, attributeId } of projectRootAttributesInDisplayOrder(
|
|
13462
|
+
document.project
|
|
13463
|
+
)) {
|
|
13464
|
+
const attribute = attributeById.get(attributeId);
|
|
13465
|
+
const containerId = attribute !== void 0 && typeof attribute.valueId === "string" ? attribute.valueId : null;
|
|
13466
|
+
if (root.key === "Save") saveOwned = reachable(containerId);
|
|
13467
|
+
if (root.key === "Session") sessionOwned = reachable(containerId);
|
|
13468
|
+
}
|
|
13469
|
+
const locById = /* @__PURE__ */ new Map();
|
|
13470
|
+
const texts = Array.isArray(raw.localizedTexts) ? raw.localizedTexts.filter(isObjectRecord2) : [];
|
|
13471
|
+
for (const text of texts) {
|
|
13472
|
+
const locales = isObjectRecord2(text.localeValues) ? text.localeValues : {};
|
|
13473
|
+
const main2 = isObjectRecord2(locales["en-US"]) ? locales["en-US"] : {};
|
|
13474
|
+
locById.set(
|
|
13475
|
+
String(text.id),
|
|
13476
|
+
typeof main2.value === "string" ? main2.value : null
|
|
13477
|
+
);
|
|
13478
|
+
}
|
|
13479
|
+
const lookupKeys = /* @__PURE__ */ new Set();
|
|
13480
|
+
const lookupAttributeIds = new Set(
|
|
13481
|
+
document.attributes.filter((attribute) => attribute.type === 9).map((attribute) => String(attribute.id))
|
|
13482
|
+
);
|
|
13483
|
+
for (const type of document.types) {
|
|
13484
|
+
if (!isObjectRecord2(type.schema)) continue;
|
|
13485
|
+
for (const [key, attributeId] of Object.entries(type.schema)) {
|
|
13486
|
+
if (typeof attributeId === "string" && lookupAttributeIds.has(attributeId)) {
|
|
13487
|
+
lookupKeys.add(key);
|
|
13488
|
+
}
|
|
13489
|
+
}
|
|
13490
|
+
}
|
|
13491
|
+
return {
|
|
13492
|
+
valueById,
|
|
13493
|
+
rootValue: buildRootValue(document),
|
|
13494
|
+
vm: {
|
|
13495
|
+
project: document.project,
|
|
13496
|
+
attributes: document.attributes,
|
|
13497
|
+
values: document.values,
|
|
13498
|
+
types: document.types,
|
|
13499
|
+
enums: document.enums,
|
|
13500
|
+
projectFiles: document.projectFiles,
|
|
13501
|
+
localizationConfig: document.localizationConfig,
|
|
13502
|
+
localizedTexts: document.localizedTexts,
|
|
13503
|
+
databaseVM: makeEvaluatorLookups(
|
|
13504
|
+
document.attributes,
|
|
13505
|
+
document.values
|
|
13506
|
+
)
|
|
13507
|
+
},
|
|
13508
|
+
saveOwned,
|
|
13509
|
+
sessionOwned,
|
|
13510
|
+
locById,
|
|
13511
|
+
lookupKeys
|
|
13512
|
+
};
|
|
13513
|
+
}
|
|
13514
|
+
function dryrunDialogue(dialogue, world) {
|
|
13515
|
+
const name = String(dialogue.name ?? dialogue.id);
|
|
13516
|
+
const nodes = isObjectRecord2(dialogue.nodes) ? dialogue.nodes : {};
|
|
13517
|
+
const report = {
|
|
13518
|
+
dialogue: name,
|
|
13519
|
+
dialogueId: String(dialogue.id),
|
|
13520
|
+
nodesVisited: 0,
|
|
13521
|
+
nodesTotal: Object.keys(nodes).length,
|
|
13522
|
+
pathsWalked: 0,
|
|
13523
|
+
endings: 0,
|
|
13524
|
+
actionsEvaluated: 0,
|
|
13525
|
+
violations: [],
|
|
13526
|
+
warnings: []
|
|
13527
|
+
};
|
|
13528
|
+
const violate = (node, message) => {
|
|
13529
|
+
report.violations.push({ dialogue: name, node, message });
|
|
13530
|
+
};
|
|
13531
|
+
const trigger = isObjectRecord2(dialogue.triggerNode) ? dialogue.triggerNode : null;
|
|
13532
|
+
const start = trigger?.toNodeId;
|
|
13533
|
+
if (typeof start !== "string") {
|
|
13534
|
+
report.warnings.push("no trigger entry point \u2014 dialogue can never fire");
|
|
13535
|
+
return report;
|
|
13536
|
+
}
|
|
13537
|
+
const visited = /* @__PURE__ */ new Set();
|
|
13538
|
+
const evaluatedActionNodes = /* @__PURE__ */ new Set();
|
|
13539
|
+
const walk = (nodeId, pathSeen) => {
|
|
13540
|
+
const node = nodes[nodeId];
|
|
13541
|
+
if (node === void 0) {
|
|
13542
|
+
violate(nodeId.slice(0, 8), "edge points at a node that does not exist");
|
|
13543
|
+
return;
|
|
13544
|
+
}
|
|
13545
|
+
if (pathSeen.has(nodeId)) return;
|
|
13546
|
+
const nextSeen = new Set(pathSeen);
|
|
13547
|
+
nextSeen.add(nodeId);
|
|
13548
|
+
visited.add(nodeId);
|
|
13549
|
+
const label = String(node.name ?? nodeId.slice(0, 8));
|
|
13550
|
+
if (typeof node.text === "string") {
|
|
13551
|
+
const value = world.locById.get(node.text);
|
|
13552
|
+
if (value === void 0) violate(label, "node text record is missing");
|
|
13553
|
+
else if (value === null || value === "")
|
|
13554
|
+
violate(label, "node text has no en-US value");
|
|
13555
|
+
}
|
|
13556
|
+
if (node.type === 2 && !evaluatedActionNodes.has(nodeId)) {
|
|
13557
|
+
evaluatedActionNodes.add(nodeId);
|
|
13558
|
+
runActions(node, label, world, report, violate);
|
|
13559
|
+
}
|
|
13560
|
+
const edges = [];
|
|
13561
|
+
if (typeof node.toNodeId === "string") edges.push(node.toNodeId);
|
|
13562
|
+
const optionSettings = isObjectRecord2(node.optionSettings) ? node.optionSettings : null;
|
|
13563
|
+
const options = Array.isArray(optionSettings?.options) ? optionSettings.options.filter(isObjectRecord2) : [];
|
|
13564
|
+
for (const option of options) {
|
|
13565
|
+
const text = typeof option.text === "string" ? world.locById.get(option.text) : void 0;
|
|
13566
|
+
if (text === void 0)
|
|
13567
|
+
violate(label, `option "${String(option.name)}" text record missing`);
|
|
13568
|
+
else if (text === null || text === "")
|
|
13569
|
+
violate(label, `option "${String(option.name)}" has no en-US value`);
|
|
13570
|
+
if (typeof option.toNodeId === "string") edges.push(option.toNodeId);
|
|
13571
|
+
else violate(label, `option "${String(option.name)}" leads nowhere`);
|
|
13572
|
+
}
|
|
13573
|
+
const outcomes = Array.isArray(node.outcomes) ? node.outcomes.filter(isObjectRecord2) : [];
|
|
13574
|
+
for (const outcome of outcomes) {
|
|
13575
|
+
runOutcomeConditions(node, outcome, label, world, report, violate);
|
|
13576
|
+
if (typeof outcome.toNodeId === "string") edges.push(outcome.toNodeId);
|
|
13577
|
+
}
|
|
13578
|
+
if (edges.length === 0) {
|
|
13579
|
+
report.pathsWalked += 1;
|
|
13580
|
+
report.endings += 1;
|
|
13581
|
+
return;
|
|
13582
|
+
}
|
|
13583
|
+
for (const edge of edges) walk(edge, nextSeen);
|
|
13584
|
+
};
|
|
13585
|
+
walk(start, /* @__PURE__ */ new Set());
|
|
13586
|
+
report.nodesVisited = visited.size;
|
|
13587
|
+
for (const [nodeId, node] of Object.entries(nodes)) {
|
|
13588
|
+
if (!visited.has(nodeId)) {
|
|
13589
|
+
report.warnings.push(
|
|
13590
|
+
`unreachable node "${String(node.name ?? nodeId.slice(0, 8))}"`
|
|
13591
|
+
);
|
|
13592
|
+
}
|
|
13593
|
+
}
|
|
13594
|
+
return report;
|
|
13595
|
+
}
|
|
13596
|
+
function evaluatorContext(node, world) {
|
|
13597
|
+
const primary = typeof node.primaryLinkedValueId === "string" ? node.primaryLinkedValueId : null;
|
|
13598
|
+
const thisRecord = primary === null ? null : world.valueById.get(primary);
|
|
13599
|
+
return {
|
|
13600
|
+
vm: world.vm,
|
|
13601
|
+
thisValue: thisRecord?.value ?? null,
|
|
13602
|
+
rootValue: world.rootValue,
|
|
13603
|
+
dialogueContext: null
|
|
13604
|
+
};
|
|
13605
|
+
}
|
|
13606
|
+
function runActions(node, label, world, report, violate) {
|
|
13607
|
+
const actions = Array.isArray(node.actions) ? node.actions.filter(isObjectRecord2) : [];
|
|
13608
|
+
const ctx = evaluatorContext(node, world);
|
|
13609
|
+
for (const action of actions) {
|
|
13610
|
+
const logic = isObjectRecord2(action.logic) ? action.logic : null;
|
|
13611
|
+
const compiled = logic !== null ? logic.action : void 0;
|
|
13612
|
+
if (!isObjectRecord2(compiled)) {
|
|
13613
|
+
report.warnings.push(
|
|
13614
|
+
`"${label}" has an action with no compiled logic (no-op)`
|
|
13615
|
+
);
|
|
13616
|
+
continue;
|
|
13617
|
+
}
|
|
13618
|
+
report.actionsEvaluated += 1;
|
|
13619
|
+
checkLookupMutationArgs(compiled, label, world, violate);
|
|
13620
|
+
let writes;
|
|
13621
|
+
try {
|
|
13622
|
+
writes = evaluateNSAction(compiled, ctx).writes;
|
|
13623
|
+
} catch (error) {
|
|
13624
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
13625
|
+
if (message.includes("no native delegate is available")) {
|
|
13626
|
+
report.warnings.push(
|
|
13627
|
+
`"${label}" calls a native function \u2014 skipped in simulation (${message.split("'")[1] ?? "native"})`
|
|
13628
|
+
);
|
|
13629
|
+
} else {
|
|
13630
|
+
violate(label, `action failed to evaluate: ${message}`);
|
|
13631
|
+
}
|
|
13632
|
+
continue;
|
|
13633
|
+
}
|
|
13634
|
+
for (const write of writes) {
|
|
13635
|
+
checkWriteOwnership(write, compiled, label, ctx, world, violate);
|
|
13636
|
+
}
|
|
13637
|
+
}
|
|
13638
|
+
}
|
|
13639
|
+
function runOutcomeConditions(node, outcome, label, world, report, violate) {
|
|
13640
|
+
const conditions = Array.isArray(outcome.conditions) ? outcome.conditions.filter(isObjectRecord2) : [];
|
|
13641
|
+
const ctx = evaluatorContext(node, world);
|
|
13642
|
+
for (const condition of conditions) {
|
|
13643
|
+
if (!isObjectRecord2(condition.getter)) continue;
|
|
13644
|
+
report.actionsEvaluated += 1;
|
|
13645
|
+
try {
|
|
13646
|
+
const value = evaluateNSGetter(condition.getter, ctx);
|
|
13647
|
+
if (typeof value !== "boolean") {
|
|
13648
|
+
violate(label, `outcome condition returned ${typeof value}, not bool`);
|
|
13649
|
+
}
|
|
13650
|
+
} catch (error) {
|
|
13651
|
+
violate(
|
|
13652
|
+
label,
|
|
13653
|
+
`outcome condition failed: ${error instanceof Error ? error.message : String(error)}`
|
|
13654
|
+
);
|
|
13655
|
+
}
|
|
13656
|
+
}
|
|
13657
|
+
}
|
|
13658
|
+
function checkWriteOwnership(write, compiledAction, label, ctx, world, violate) {
|
|
13659
|
+
const target = isObjectRecord2(write.target) ? write.target : null;
|
|
13660
|
+
if (target === null) {
|
|
13661
|
+
violate(label, `${write.kind} write has no target`);
|
|
13662
|
+
return;
|
|
13663
|
+
}
|
|
13664
|
+
if (write.kind !== "assign") {
|
|
13665
|
+
const literalKey = targetLiteralKey(target);
|
|
13666
|
+
if (literalKey !== null && world.lookupKeys.has(literalKey)) return;
|
|
13667
|
+
}
|
|
13668
|
+
const writability = typeof target.writability === "string" ? target.writability : null;
|
|
13669
|
+
const sessionLike = writability === "session" || writability === "assetToSessionLookup" || writability === "local";
|
|
13670
|
+
const saveLike = writability === "save" || writability === "assetToSaveLookup";
|
|
13671
|
+
if (writability !== null && !saveLike && !sessionLike) {
|
|
13672
|
+
violate(
|
|
13673
|
+
label,
|
|
13674
|
+
`${write.kind} write targets a ${writability} value \u2014 the runtime refuses non save/session mutations`
|
|
13675
|
+
);
|
|
13676
|
+
return;
|
|
13677
|
+
}
|
|
13678
|
+
const rowId = resolveWriteRowId(target, compiledAction, ctx, world);
|
|
13679
|
+
if (rowId === null) {
|
|
13680
|
+
violate(
|
|
13681
|
+
label,
|
|
13682
|
+
`${write.kind} write receiver did not resolve to a stored value row \u2014 null lookup (missing save-map entry?) or a lookup-resolved asset collection; the runtime cannot apply this write`
|
|
13683
|
+
);
|
|
13684
|
+
return;
|
|
13685
|
+
}
|
|
13686
|
+
const owned = sessionLike ? world.sessionOwned.has(rowId) : world.saveOwned.has(rowId);
|
|
13687
|
+
if (!owned) {
|
|
13688
|
+
violate(
|
|
13689
|
+
label,
|
|
13690
|
+
`${write.kind} write lands on '${rowId}' which is not ${sessionLike ? "session" : "save"}-owned \u2014 the runtime will throw exactly here`
|
|
13691
|
+
);
|
|
13692
|
+
}
|
|
13693
|
+
}
|
|
13694
|
+
function checkLookupMutationArgs(compiledAction, label, world, violate) {
|
|
13695
|
+
const scan = (value) => {
|
|
13696
|
+
if (Array.isArray(value)) {
|
|
13697
|
+
for (const entry of value) scan(entry);
|
|
13698
|
+
return;
|
|
13699
|
+
}
|
|
13700
|
+
if (!isObjectRecord2(value)) return;
|
|
13701
|
+
const mutation = value.mutation;
|
|
13702
|
+
const target = isObjectRecord2(value.target) ? value.target : null;
|
|
13703
|
+
if (typeof mutation === "string" && target !== null) {
|
|
13704
|
+
const literalKey = targetLiteralKey(target);
|
|
13705
|
+
if (literalKey !== null && world.lookupKeys.has(literalKey)) {
|
|
13706
|
+
const args = Array.isArray(value.args) ? value.args : [];
|
|
13707
|
+
for (const arg of args) {
|
|
13708
|
+
if (isObjectRecord2(arg) && arg.type === "value" && isObjectRecord2(arg.value) && typeof arg.value.value === "string") {
|
|
13709
|
+
violate(
|
|
13710
|
+
label,
|
|
13711
|
+
`${mutation} on lookup "${literalKey}" passes an id string literal \u2014 the runtime needs a collection entry (bind the node to the item and use Add(this))`
|
|
13712
|
+
);
|
|
13713
|
+
}
|
|
13714
|
+
}
|
|
13715
|
+
}
|
|
13716
|
+
}
|
|
13717
|
+
for (const child of Object.values(value)) scan(child);
|
|
13718
|
+
};
|
|
13719
|
+
scan(compiledAction.instructions);
|
|
13720
|
+
}
|
|
13721
|
+
function targetLiteralKey(target) {
|
|
13722
|
+
const pointer = isObjectRecord2(target.pointer) ? target.pointer : target;
|
|
13723
|
+
if (pointer.type !== "keyOf" || !isObjectRecord2(pointer.keyOf)) return null;
|
|
13724
|
+
const key = pointer.keyOf.key;
|
|
13725
|
+
if (!isObjectRecord2(key) || !isObjectRecord2(key.value)) return null;
|
|
13726
|
+
return typeof key.value.value === "string" ? key.value.value : null;
|
|
13727
|
+
}
|
|
13728
|
+
function resolveWriteRowId(target, compiledAction, ctx, world) {
|
|
13729
|
+
const pointer = isObjectRecord2(target.pointer) ? target.pointer : target;
|
|
13730
|
+
const evalPointer2 = (p) => {
|
|
13731
|
+
const getter = {
|
|
13732
|
+
parameters: compiledAction.parameters ?? [],
|
|
13733
|
+
instructions: [{ type: "return", pointer: p }],
|
|
13734
|
+
typeInfo: { type: 0, required: false }
|
|
13735
|
+
};
|
|
13736
|
+
try {
|
|
13737
|
+
return evaluateNSGetter(getter, ctx);
|
|
13738
|
+
} catch {
|
|
13739
|
+
return void 0;
|
|
13740
|
+
}
|
|
13741
|
+
};
|
|
13742
|
+
if (pointer.type === "keyOf" && isObjectRecord2(pointer.keyOf)) {
|
|
13743
|
+
const keyOf = pointer.keyOf;
|
|
13744
|
+
const key = isObjectRecord2(keyOf.key) ? keyOf.key : null;
|
|
13745
|
+
const literalKey = key !== null && isObjectRecord2(key.value) ? key.value.value : void 0;
|
|
13746
|
+
const receiver2 = evalPointer2(keyOf.pointer);
|
|
13747
|
+
if (isObjectRecord2(receiver2) && typeof literalKey === "string") {
|
|
13748
|
+
const child = receiver2[literalKey];
|
|
13749
|
+
if (typeof child === "string" && world.valueById.has(child)) {
|
|
13750
|
+
return child;
|
|
13751
|
+
}
|
|
13752
|
+
}
|
|
13753
|
+
return null;
|
|
13754
|
+
}
|
|
13755
|
+
if (pointer.type === "reference" && typeof pointer.valueId === "string") {
|
|
13756
|
+
return pointer.valueId;
|
|
13757
|
+
}
|
|
13758
|
+
const receiver = evalPointer2(pointer);
|
|
13759
|
+
if (receiver !== void 0 && receiver !== null) {
|
|
13760
|
+
for (const [id, record] of world.valueById) {
|
|
13761
|
+
if (record.value === receiver) return id;
|
|
13762
|
+
}
|
|
13763
|
+
}
|
|
13764
|
+
return null;
|
|
13765
|
+
}
|
|
13766
|
+
function printReport(report) {
|
|
13767
|
+
const headline = `${report.dialogue} ${color.dim(
|
|
13768
|
+
`${report.nodesVisited}/${report.nodesTotal} nodes, ${report.endings} ending(s), ${report.actionsEvaluated} action/condition eval(s)`
|
|
13769
|
+
)}`;
|
|
13770
|
+
if (report.violations.length === 0) {
|
|
13771
|
+
console.log(`${sym.ok} ${headline}`);
|
|
13772
|
+
} else {
|
|
13773
|
+
console.log(`${sym.fail} ${headline}`);
|
|
13774
|
+
for (const violation of report.violations) {
|
|
13775
|
+
console.log(` ${color.red(violation.node)}: ${violation.message}`);
|
|
13776
|
+
}
|
|
13777
|
+
}
|
|
13778
|
+
for (const warning of report.warnings) {
|
|
13779
|
+
console.log(` ${sym.warn} ${color.dim(warning)}`);
|
|
13780
|
+
}
|
|
13781
|
+
}
|
|
13782
|
+
var init_dialogue_dryrun = __esm({
|
|
13783
|
+
"src/commands/dialogue-dryrun.ts"() {
|
|
13784
|
+
"use strict";
|
|
13785
|
+
init_document();
|
|
13786
|
+
init_projection();
|
|
13787
|
+
init_neoscript_evaluator();
|
|
13788
|
+
init_project_root_attributes();
|
|
13789
|
+
init_ui();
|
|
13790
|
+
init_script();
|
|
13791
|
+
}
|
|
13792
|
+
});
|
|
13793
|
+
|
|
13794
|
+
// src/commands/content.ts
|
|
13795
|
+
var content_exports = {};
|
|
13796
|
+
__export(content_exports, {
|
|
13797
|
+
makeContentContext: () => makeContentContext,
|
|
13798
|
+
runDialogue: () => runDialogue,
|
|
13799
|
+
runFiles: () => runFiles,
|
|
13800
|
+
runLoc: () => runLoc,
|
|
13801
|
+
runRecords: () => runRecords,
|
|
13802
|
+
runValues: () => runValues
|
|
13803
|
+
});
|
|
13804
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
13805
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
13806
|
+
function versionPath(workspace, suffix) {
|
|
13807
|
+
return `/api/projects/${workspace.config.projectId}/versions/${workspace.config.versionId}/${suffix}`;
|
|
13808
|
+
}
|
|
13809
|
+
function readBatch(file) {
|
|
13810
|
+
let raw = null;
|
|
13811
|
+
if (file !== null) {
|
|
13812
|
+
raw = readFileSync10(file, "utf8");
|
|
13813
|
+
} else if (!process.stdin.isTTY) {
|
|
13814
|
+
raw = readFileSync10(0, "utf8");
|
|
13815
|
+
if (raw.trim().length === 0) raw = null;
|
|
13816
|
+
}
|
|
13817
|
+
if (raw === null) return null;
|
|
13818
|
+
const parsed = JSON.parse(raw);
|
|
13819
|
+
if (!Array.isArray(parsed)) {
|
|
13820
|
+
throw new Error("Batch input must be a JSON array.");
|
|
13821
|
+
}
|
|
13822
|
+
return parsed;
|
|
13823
|
+
}
|
|
13824
|
+
async function runRecords(context, subcommand, positional, filterKind) {
|
|
13825
|
+
const { raw, records, heads } = await fetchProjectDocument(context.workspace);
|
|
13826
|
+
if (subcommand === "get") {
|
|
13827
|
+
const [recordKind, recordId] = positional;
|
|
13828
|
+
if (recordKind === void 0 || recordId === void 0) {
|
|
13829
|
+
throw new Error("Usage: neo records get <recordKind> <recordId>");
|
|
13830
|
+
}
|
|
13831
|
+
const fromMap = records.get(`${recordKind}:${recordId}`);
|
|
13832
|
+
if (fromMap !== void 0) {
|
|
13833
|
+
console.log(JSON.stringify(fromMap, null, 2));
|
|
13834
|
+
return;
|
|
13835
|
+
}
|
|
13836
|
+
for (const value of Object.values(raw)) {
|
|
13837
|
+
if (!Array.isArray(value)) continue;
|
|
13838
|
+
for (const entry of value) {
|
|
13839
|
+
if (isObjectRecord2(entry) && entry.id === recordId) {
|
|
13840
|
+
console.log(JSON.stringify(entry, null, 2));
|
|
13841
|
+
return;
|
|
13842
|
+
}
|
|
13843
|
+
}
|
|
13844
|
+
}
|
|
13845
|
+
throw new Error(`Record "${recordKind}:${recordId}" was not found.`);
|
|
13846
|
+
}
|
|
13847
|
+
if (subcommand === "query") {
|
|
13848
|
+
const result = [];
|
|
13849
|
+
for (const head of heads.values()) {
|
|
13850
|
+
if (filterKind !== null && head.recordKind !== filterKind) continue;
|
|
13851
|
+
result.push({ ...head });
|
|
13852
|
+
}
|
|
13853
|
+
console.log(JSON.stringify(result, null, 2));
|
|
13854
|
+
return;
|
|
13855
|
+
}
|
|
13856
|
+
throw new Error(`Unknown records subcommand "${subcommand}".`);
|
|
13857
|
+
}
|
|
13858
|
+
async function runValues(context, subcommand, positional, file) {
|
|
13859
|
+
if (subcommand === "get" || subcommand === "list") {
|
|
13860
|
+
const { raw } = await fetchProjectDocument(context.workspace);
|
|
13861
|
+
const values = Array.isArray(raw.values) ? raw.values.filter(isObjectRecord2) : [];
|
|
13862
|
+
if (subcommand === "get") {
|
|
13863
|
+
const valueId = positional[0];
|
|
13864
|
+
if (valueId === void 0)
|
|
13865
|
+
throw new Error("Usage: neo values get <valueId>");
|
|
13866
|
+
const value = values.find((entry) => entry.id === valueId);
|
|
13867
|
+
if (value === void 0)
|
|
13868
|
+
throw new Error(`Value "${valueId}" was not found.`);
|
|
13869
|
+
console.log(JSON.stringify(value, null, 2));
|
|
13870
|
+
return;
|
|
13871
|
+
}
|
|
13872
|
+
const attributeId = positional[0] ?? null;
|
|
13873
|
+
const filtered = attributeId === null ? values : values.filter((entry) => entry.attributeId === attributeId);
|
|
13874
|
+
console.log(
|
|
13875
|
+
JSON.stringify(
|
|
13876
|
+
filtered.map((entry) => ({
|
|
13877
|
+
id: entry.id,
|
|
13878
|
+
attributeId: entry.attributeId,
|
|
13879
|
+
value: entry.value
|
|
13880
|
+
})),
|
|
13881
|
+
null,
|
|
13882
|
+
2
|
|
13883
|
+
)
|
|
13884
|
+
);
|
|
13885
|
+
return;
|
|
13886
|
+
}
|
|
13887
|
+
if (subcommand === "add-entry" || subcommand === "delete-entry") {
|
|
13888
|
+
const containerValueId = positional[0];
|
|
13889
|
+
const jsonArg = positional[1];
|
|
13890
|
+
if (containerValueId === void 0 || jsonArg === void 0) {
|
|
13891
|
+
throw new Error(
|
|
13892
|
+
`Usage: neo values ${subcommand} <containerValueId> '<json>'`
|
|
13893
|
+
);
|
|
13894
|
+
}
|
|
13895
|
+
const verb = subcommand === "add-entry" ? "add" : "delete";
|
|
13896
|
+
const result = await context.client.post(
|
|
13897
|
+
versionPath(
|
|
13898
|
+
context.workspace,
|
|
13899
|
+
`values/${containerValueId}/entries/${verb}`
|
|
13900
|
+
),
|
|
13901
|
+
JSON.parse(jsonArg)
|
|
13902
|
+
);
|
|
13903
|
+
console.log(JSON.stringify(result, null, 2));
|
|
13904
|
+
return;
|
|
13905
|
+
}
|
|
13906
|
+
if (subcommand === "create") {
|
|
13907
|
+
const jsonArg = positional[0];
|
|
13908
|
+
if (jsonArg === void 0) {
|
|
13909
|
+
throw new Error(
|
|
13910
|
+
"Usage: neo values create '<valueJson>' [--into <containerValueId> --key <AttributeName>]"
|
|
13911
|
+
);
|
|
13912
|
+
}
|
|
13913
|
+
const intoIndex = process.argv.indexOf("--into");
|
|
13914
|
+
const keyIndex = process.argv.indexOf("--key");
|
|
13915
|
+
const containerValueId = intoIndex >= 0 ? process.argv[intoIndex + 1] ?? null : null;
|
|
13916
|
+
const containerKey = keyIndex >= 0 ? process.argv[keyIndex + 1] ?? null : null;
|
|
13917
|
+
if (containerValueId === null !== (containerKey === null)) {
|
|
13918
|
+
throw new Error(
|
|
13919
|
+
"values create needs BOTH --into and --key (or neither)."
|
|
13920
|
+
);
|
|
13921
|
+
}
|
|
13922
|
+
const newValueId = randomUUID5();
|
|
13923
|
+
const { heads, raw } = await fetchProjectDocument(context.workspace);
|
|
13924
|
+
const changes = [
|
|
13925
|
+
{
|
|
13926
|
+
recordKind: "value",
|
|
13927
|
+
recordId: newValueId,
|
|
13928
|
+
operation: "create",
|
|
13929
|
+
// Full IAttributeValueProps shape — the export read bundle validates
|
|
13930
|
+
// every value record (id/projectId/value/createdAt/updatedAt).
|
|
13931
|
+
nextData: {
|
|
13932
|
+
id: newValueId,
|
|
13933
|
+
projectId: context.workspace.config.projectId,
|
|
13934
|
+
value: JSON.parse(jsonArg),
|
|
13935
|
+
createdAt: Date.now(),
|
|
13936
|
+
updatedAt: Date.now()
|
|
13937
|
+
},
|
|
13938
|
+
intent: createProjectVersionIntent("value.create", {
|
|
13939
|
+
source: "neo-cli-values"
|
|
13940
|
+
}),
|
|
13941
|
+
expectedBaseContentHash: null
|
|
13942
|
+
}
|
|
13943
|
+
];
|
|
13944
|
+
if (containerValueId !== null && containerKey !== null) {
|
|
13945
|
+
const values = Array.isArray(raw.values) ? raw.values.filter(isObjectRecord2) : [];
|
|
13946
|
+
const container = values.find((value) => value.id === containerValueId);
|
|
13947
|
+
if (container === void 0) {
|
|
13948
|
+
throw new Error(`Container value "${containerValueId}" was not found.`);
|
|
13949
|
+
}
|
|
13950
|
+
if (!isObjectRecord2(container.value)) {
|
|
13951
|
+
throw new Error(
|
|
13952
|
+
`Container value "${containerValueId}" is not an attribute map.`
|
|
13953
|
+
);
|
|
13954
|
+
}
|
|
13955
|
+
changes.push({
|
|
13956
|
+
recordKind: "value",
|
|
13957
|
+
recordId: containerValueId,
|
|
13958
|
+
operation: "update",
|
|
13959
|
+
nextData: {
|
|
13960
|
+
...container,
|
|
13961
|
+
value: { ...container.value, [containerKey]: newValueId }
|
|
13962
|
+
},
|
|
13963
|
+
intent: createProjectVersionIntent("value.update", {
|
|
13964
|
+
source: "neo-cli-values"
|
|
13965
|
+
}),
|
|
13966
|
+
expectedBaseContentHash: heads.get(`value:${containerValueId}`)?.contentHash ?? null
|
|
13967
|
+
});
|
|
13968
|
+
}
|
|
13969
|
+
const convex = await createConvexClient(context.workspace);
|
|
13970
|
+
await convex.mutation(api.serverProjectRecordOperations.commitFromSession, {
|
|
13971
|
+
projectId: context.workspace.config.projectId,
|
|
13972
|
+
versionId: context.workspace.config.versionId,
|
|
13973
|
+
operation: "create",
|
|
13974
|
+
changes,
|
|
13975
|
+
summary: "neo values create",
|
|
13976
|
+
force: true
|
|
13977
|
+
});
|
|
13978
|
+
console.log(newValueId);
|
|
13979
|
+
return;
|
|
13980
|
+
}
|
|
13981
|
+
if (subcommand === "set") {
|
|
13982
|
+
const batch = readBatch(file);
|
|
13983
|
+
const edits = [];
|
|
13984
|
+
if (batch !== null) {
|
|
13985
|
+
for (const entry of batch) {
|
|
13986
|
+
if (!isObjectRecord2(entry) || typeof entry.valueId !== "string") {
|
|
13987
|
+
throw new Error(
|
|
13988
|
+
'values set batch entries need {"valueId", "value"}.'
|
|
13989
|
+
);
|
|
13990
|
+
}
|
|
13991
|
+
edits.push({ valueId: entry.valueId, value: entry.value });
|
|
13992
|
+
}
|
|
13993
|
+
} else {
|
|
13994
|
+
const [valueId, valueJson] = positional;
|
|
13995
|
+
if (valueId === void 0 || valueJson === void 0) {
|
|
13996
|
+
throw new Error(
|
|
13997
|
+
"Usage: neo values set <valueId> <valueJson> (or batch via stdin/--file)"
|
|
13998
|
+
);
|
|
13999
|
+
}
|
|
14000
|
+
edits.push({ valueId, value: JSON.parse(valueJson) });
|
|
14001
|
+
}
|
|
14002
|
+
const { heads, raw } = await fetchProjectDocument(context.workspace);
|
|
14003
|
+
const valueById = new Map(
|
|
14004
|
+
(Array.isArray(raw.values) ? raw.values.filter(isObjectRecord2) : []).map(
|
|
14005
|
+
(value) => [String(value.id), value]
|
|
14006
|
+
)
|
|
14007
|
+
);
|
|
14008
|
+
const now = Date.now();
|
|
14009
|
+
const convex = await createConvexClient(context.workspace);
|
|
14010
|
+
await convex.mutation(api.serverProjectRecordOperations.commitFromSession, {
|
|
14011
|
+
projectId: context.workspace.config.projectId,
|
|
14012
|
+
versionId: context.workspace.config.versionId,
|
|
14013
|
+
operation: "update",
|
|
14014
|
+
changes: edits.map((edit) => {
|
|
14015
|
+
const record = valueById.get(edit.valueId);
|
|
14016
|
+
if (record === void 0) {
|
|
14017
|
+
throw new Error(`Value "${edit.valueId}" was not found.`);
|
|
14018
|
+
}
|
|
14019
|
+
return {
|
|
14020
|
+
recordKind: "value",
|
|
14021
|
+
recordId: edit.valueId,
|
|
12410
14022
|
operation: "update",
|
|
12411
|
-
|
|
14023
|
+
// Backfill the required record envelope (repairs rows minted by
|
|
14024
|
+
// older CLI builds that wrote only {id, value}).
|
|
14025
|
+
nextData: {
|
|
14026
|
+
projectId: context.workspace.config.projectId,
|
|
14027
|
+
createdAt: now,
|
|
14028
|
+
...record,
|
|
14029
|
+
value: edit.value,
|
|
14030
|
+
updatedAt: now
|
|
14031
|
+
},
|
|
12412
14032
|
intent: createProjectVersionIntent("value.update", {
|
|
12413
14033
|
source: "neo-cli-values"
|
|
12414
14034
|
}),
|
|
@@ -12471,6 +14091,107 @@ async function runLoc(context, subcommand, positional, file) {
|
|
|
12471
14091
|
}
|
|
12472
14092
|
return;
|
|
12473
14093
|
}
|
|
14094
|
+
if (subcommand === "create") {
|
|
14095
|
+
const jsonArg = positional[0];
|
|
14096
|
+
if (jsonArg === void 0 || !jsonArg.trim().startsWith("{")) {
|
|
14097
|
+
throw new Error(
|
|
14098
|
+
`Usage: neo loc create '{"value":"...","locale":"en-US","links":[...]}'`
|
|
14099
|
+
);
|
|
14100
|
+
}
|
|
14101
|
+
const body = JSON.parse(jsonArg);
|
|
14102
|
+
if (typeof body.value !== "string") {
|
|
14103
|
+
throw new Error('loc create requires a string "value".');
|
|
14104
|
+
}
|
|
14105
|
+
const locale = typeof body.locale === "string" ? body.locale : "en-US";
|
|
14106
|
+
const { heads, raw } = await fetchProjectDocument(context.workspace);
|
|
14107
|
+
const config = isObjectRecord2(raw.localizationConfig) ? raw.localizationConfig : {};
|
|
14108
|
+
const statusId = config.mainLocale === locale && typeof config.mainLocaleDefaultStatusId === "string" ? config.mainLocaleDefaultStatusId : "localization-status-needs-translation";
|
|
14109
|
+
const now = Date.now();
|
|
14110
|
+
const textId = typeof body.id === "string" ? body.id : randomUUID5();
|
|
14111
|
+
const existingHead = heads.get(`localized-text:${textId}`);
|
|
14112
|
+
const text = {
|
|
14113
|
+
id: textId,
|
|
14114
|
+
projectId: context.workspace.config.projectId,
|
|
14115
|
+
sourceComment: typeof body.sourceComment === "string" ? body.sourceComment : null,
|
|
14116
|
+
links: Array.isArray(body.links) ? body.links : [],
|
|
14117
|
+
localeValues: {
|
|
14118
|
+
[locale]: {
|
|
14119
|
+
value: body.value,
|
|
14120
|
+
statusId,
|
|
14121
|
+
localeComment: null,
|
|
14122
|
+
updatedAt: now
|
|
14123
|
+
}
|
|
14124
|
+
},
|
|
14125
|
+
createdAt: now,
|
|
14126
|
+
updatedAt: now
|
|
14127
|
+
};
|
|
14128
|
+
const operation = existingHead === void 0 ? "create" : "update";
|
|
14129
|
+
const convex = await createConvexClient(context.workspace);
|
|
14130
|
+
await convex.mutation(api.serverProjectRecordOperations.commitFromSession, {
|
|
14131
|
+
projectId: context.workspace.config.projectId,
|
|
14132
|
+
versionId: context.workspace.config.versionId,
|
|
14133
|
+
operation,
|
|
14134
|
+
changes: [
|
|
14135
|
+
{
|
|
14136
|
+
recordKind: "localized-text",
|
|
14137
|
+
recordId: textId,
|
|
14138
|
+
operation,
|
|
14139
|
+
nextData: text,
|
|
14140
|
+
intent: createProjectVersionIntent(`localized-text.${operation}`, {
|
|
14141
|
+
source: "neo-cli-loc"
|
|
14142
|
+
}),
|
|
14143
|
+
expectedBaseContentHash: existingHead?.contentHash ?? null
|
|
14144
|
+
}
|
|
14145
|
+
],
|
|
14146
|
+
summary: `neo loc ${operation}`
|
|
14147
|
+
});
|
|
14148
|
+
console.log(textId);
|
|
14149
|
+
return;
|
|
14150
|
+
}
|
|
14151
|
+
if (subcommand === "delete") {
|
|
14152
|
+
const textId = positional[0];
|
|
14153
|
+
if (textId === void 0) {
|
|
14154
|
+
throw new Error("Usage: neo loc delete <textId>");
|
|
14155
|
+
}
|
|
14156
|
+
const { heads } = await fetchProjectDocument(context.workspace);
|
|
14157
|
+
const head = heads.get(`localized-text:${textId}`);
|
|
14158
|
+
if (head === void 0) {
|
|
14159
|
+
throw new Error(`Localized text "${textId}" was not found.`);
|
|
14160
|
+
}
|
|
14161
|
+
const convex = await createConvexClient(context.workspace);
|
|
14162
|
+
await convex.mutation(api.serverProjectRecordOperations.commitFromSession, {
|
|
14163
|
+
projectId: context.workspace.config.projectId,
|
|
14164
|
+
versionId: context.workspace.config.versionId,
|
|
14165
|
+
operation: "delete",
|
|
14166
|
+
changes: [
|
|
14167
|
+
{
|
|
14168
|
+
recordKind: "localized-text",
|
|
14169
|
+
recordId: textId,
|
|
14170
|
+
operation: "delete",
|
|
14171
|
+
deleted: true,
|
|
14172
|
+
intent: createProjectVersionIntent("localized-text.delete", {
|
|
14173
|
+
source: "neo-cli-loc"
|
|
14174
|
+
}),
|
|
14175
|
+
expectedBaseContentHash: head.contentHash
|
|
14176
|
+
}
|
|
14177
|
+
],
|
|
14178
|
+
summary: "neo loc delete"
|
|
14179
|
+
});
|
|
14180
|
+
console.log(`deleted ${textId}`);
|
|
14181
|
+
return;
|
|
14182
|
+
}
|
|
14183
|
+
if (subcommand === "archive" || subcommand === "restore") {
|
|
14184
|
+
const textId = positional[0];
|
|
14185
|
+
if (textId === void 0) {
|
|
14186
|
+
throw new Error(`Usage: neo loc ${subcommand} <textId>`);
|
|
14187
|
+
}
|
|
14188
|
+
const result = await context.client.post(
|
|
14189
|
+
versionPath(context.workspace, `localized-texts/${textId}/archive/edit`),
|
|
14190
|
+
{ archived: subcommand === "archive" }
|
|
14191
|
+
);
|
|
14192
|
+
console.log(JSON.stringify(result, null, 2));
|
|
14193
|
+
return;
|
|
14194
|
+
}
|
|
12474
14195
|
if (subcommand === "status-set") {
|
|
12475
14196
|
const [textId, locale, statusId] = positional;
|
|
12476
14197
|
if (textId === void 0 || locale === void 0 || statusId === void 0) {
|
|
@@ -12593,6 +14314,103 @@ async function runDialogue(context, subcommand, positional, file) {
|
|
|
12593
14314
|
`Unknown ${subcommand} verb "${String(groupVerb)}" (list | create | edit | delete).`
|
|
12594
14315
|
);
|
|
12595
14316
|
}
|
|
14317
|
+
if (subcommand === "create" || subcommand === "edit" || subcommand === "delete") {
|
|
14318
|
+
const readBody = () => {
|
|
14319
|
+
const batch = readBatch(file);
|
|
14320
|
+
if (batch !== null) {
|
|
14321
|
+
throw new Error(
|
|
14322
|
+
`neo dialogue ${subcommand} takes one JSON body, not a batch.`
|
|
14323
|
+
);
|
|
14324
|
+
}
|
|
14325
|
+
const jsonArg = positional[positional.length - 1];
|
|
14326
|
+
if (jsonArg === void 0 || !jsonArg.trim().startsWith("{")) {
|
|
14327
|
+
throw new Error(
|
|
14328
|
+
`Usage: neo dialogue ${subcommand} ${subcommand === "create" ? "'<IDialogueBase json>'" : "<dialogueId> ['<json>']"}${file === null ? " (or --file <path>)" : ""}`
|
|
14329
|
+
);
|
|
14330
|
+
}
|
|
14331
|
+
return JSON.parse(jsonArg);
|
|
14332
|
+
};
|
|
14333
|
+
if (subcommand === "create") {
|
|
14334
|
+
const result2 = await context.client.post(
|
|
14335
|
+
versionPath(context.workspace, "dialogues/create"),
|
|
14336
|
+
readBody()
|
|
14337
|
+
);
|
|
14338
|
+
console.log(JSON.stringify(result2, null, 2));
|
|
14339
|
+
return;
|
|
14340
|
+
}
|
|
14341
|
+
const dialogueId = positional[0];
|
|
14342
|
+
if (dialogueId === void 0) {
|
|
14343
|
+
throw new Error(`Usage: neo dialogue ${subcommand} <dialogueId>`);
|
|
14344
|
+
}
|
|
14345
|
+
const result = await context.client.post(
|
|
14346
|
+
versionPath(context.workspace, `dialogues/${dialogueId}/${subcommand}`),
|
|
14347
|
+
subcommand === "edit" ? readBody() : void 0
|
|
14348
|
+
);
|
|
14349
|
+
console.log(JSON.stringify(result, null, 2));
|
|
14350
|
+
return;
|
|
14351
|
+
}
|
|
14352
|
+
if (subcommand === "export") {
|
|
14353
|
+
const ref = positional[0];
|
|
14354
|
+
if (ref === void 0) {
|
|
14355
|
+
throw new Error("Usage: neo dialogue export <dialogueIdOrName>");
|
|
14356
|
+
}
|
|
14357
|
+
const { runDialogueExport: runDialogueExport2 } = await Promise.resolve().then(() => (init_dialogue_spec(), dialogue_spec_exports));
|
|
14358
|
+
await runDialogueExport2(context.workspace, ref);
|
|
14359
|
+
return;
|
|
14360
|
+
}
|
|
14361
|
+
if (subcommand === "apply") {
|
|
14362
|
+
const specFile = file ?? positional[0];
|
|
14363
|
+
if (specFile === void 0) {
|
|
14364
|
+
throw new Error(
|
|
14365
|
+
"Usage: neo dialogue apply <spec.json> [--dry-run] (or --file <path>)"
|
|
14366
|
+
);
|
|
14367
|
+
}
|
|
14368
|
+
const { runDialogueApply: runDialogueApply2 } = await Promise.resolve().then(() => (init_dialogue_spec(), dialogue_spec_exports));
|
|
14369
|
+
await runDialogueApply2(
|
|
14370
|
+
context.workspace,
|
|
14371
|
+
specFile,
|
|
14372
|
+
process.argv.includes("--dry-run")
|
|
14373
|
+
);
|
|
14374
|
+
return;
|
|
14375
|
+
}
|
|
14376
|
+
if (subcommand === "dryrun") {
|
|
14377
|
+
const { runDialogueDryrun: runDialogueDryrun2 } = await Promise.resolve().then(() => (init_dialogue_dryrun(), dialogue_dryrun_exports));
|
|
14378
|
+
await runDialogueDryrun2(
|
|
14379
|
+
context.workspace,
|
|
14380
|
+
positional[0] ?? null,
|
|
14381
|
+
context.json
|
|
14382
|
+
);
|
|
14383
|
+
return;
|
|
14384
|
+
}
|
|
14385
|
+
if (subcommand === "compile") {
|
|
14386
|
+
const jsonArg = positional[0];
|
|
14387
|
+
if (jsonArg === void 0) {
|
|
14388
|
+
throw new Error(
|
|
14389
|
+
`Usage: neo dialogue compile '{"code":"...","target":0|1|2,"node":{"primaryLinkedValueId":...}}'`
|
|
14390
|
+
);
|
|
14391
|
+
}
|
|
14392
|
+
const result = await context.client.post(
|
|
14393
|
+
versionPath(context.workspace, "dialogue-logic/compile"),
|
|
14394
|
+
JSON.parse(jsonArg)
|
|
14395
|
+
);
|
|
14396
|
+
console.log(JSON.stringify(result, null, 2));
|
|
14397
|
+
return;
|
|
14398
|
+
}
|
|
14399
|
+
if (subcommand === "text") {
|
|
14400
|
+
const dialogueId = positional[0];
|
|
14401
|
+
const jsonArg = positional[1];
|
|
14402
|
+
if (dialogueId === void 0 || jsonArg === void 0) {
|
|
14403
|
+
throw new Error(
|
|
14404
|
+
`Usage: neo dialogue text <dialogueId> '{"nodeId":...,"textId":...,"locale":"en-US","value":...,"variableIds":[]}'`
|
|
14405
|
+
);
|
|
14406
|
+
}
|
|
14407
|
+
const result = await context.client.post(
|
|
14408
|
+
versionPath(context.workspace, `dialogues/${dialogueId}/rich-text/edit`),
|
|
14409
|
+
JSON.parse(jsonArg)
|
|
14410
|
+
);
|
|
14411
|
+
console.log(JSON.stringify(result, null, 2));
|
|
14412
|
+
return;
|
|
14413
|
+
}
|
|
12596
14414
|
if (subcommand === "node") {
|
|
12597
14415
|
const nodeVerb = positional[0];
|
|
12598
14416
|
const dialogueId = positional[1];
|
|
@@ -12641,10 +14459,110 @@ async function runDialogue(context, subcommand, positional, file) {
|
|
|
12641
14459
|
);
|
|
12642
14460
|
}
|
|
12643
14461
|
throw new Error(
|
|
12644
|
-
`Unknown dialogue subcommand "${subcommand}" (list | show | node | group | priority-group).`
|
|
14462
|
+
`Unknown dialogue subcommand "${subcommand}" (list | show | export | apply | dryrun | create | edit | delete | text | compile | node | group | priority-group).`
|
|
14463
|
+
);
|
|
14464
|
+
}
|
|
14465
|
+
async function uploadProjectFile(context, filePath, preferredTemplateId) {
|
|
14466
|
+
const { readFileSync: readFile } = await import("node:fs");
|
|
14467
|
+
const { basename, extname } = await import("node:path");
|
|
14468
|
+
const bytes = readFile(filePath);
|
|
14469
|
+
const name = basename(filePath);
|
|
14470
|
+
const extension = extname(filePath).toLowerCase();
|
|
14471
|
+
const mimeByExtension = {
|
|
14472
|
+
".png": "image/png",
|
|
14473
|
+
".jpg": "image/jpeg",
|
|
14474
|
+
".jpeg": "image/jpeg",
|
|
14475
|
+
".gif": "image/gif",
|
|
14476
|
+
".wav": "audio/wav",
|
|
14477
|
+
".mp3": "audio/mpeg",
|
|
14478
|
+
".ogg": "audio/ogg"
|
|
14479
|
+
};
|
|
14480
|
+
const mimeType = mimeByExtension[extension];
|
|
14481
|
+
if (mimeType === void 0) {
|
|
14482
|
+
throw new Error(
|
|
14483
|
+
`Unsupported upload extension "${extension}" (png/jpg/gif/wav/mp3/ogg).`
|
|
14484
|
+
);
|
|
14485
|
+
}
|
|
14486
|
+
const replaceIndex = process.argv.indexOf("--replace");
|
|
14487
|
+
const replaceFileId = replaceIndex >= 0 ? process.argv[replaceIndex + 1] ?? null : null;
|
|
14488
|
+
const projectFileId = replaceFileId ?? randomUUID5();
|
|
14489
|
+
const metadata = { projectFileId };
|
|
14490
|
+
if (preferredTemplateId !== null) {
|
|
14491
|
+
metadata.preferredTemplateId = preferredTemplateId;
|
|
14492
|
+
}
|
|
14493
|
+
if (replaceFileId !== null) {
|
|
14494
|
+
metadata.replaceFileId = replaceFileId;
|
|
14495
|
+
}
|
|
14496
|
+
const presign = await context.client.post(
|
|
14497
|
+
versionPath(context.workspace, "upload"),
|
|
14498
|
+
{
|
|
14499
|
+
route: "projectFile",
|
|
14500
|
+
metadata,
|
|
14501
|
+
files: [{ name, size: bytes.byteLength, type: mimeType }]
|
|
14502
|
+
}
|
|
14503
|
+
);
|
|
14504
|
+
const entries = Array.isArray(presign.files) ? presign.files : [];
|
|
14505
|
+
const entry = entries.filter(isObjectRecord2)[0];
|
|
14506
|
+
if (entry === void 0) {
|
|
14507
|
+
throw new Error("Upload presign returned no files.");
|
|
14508
|
+
}
|
|
14509
|
+
const signedUrl = entry.signedUrl;
|
|
14510
|
+
const fileInfo = isObjectRecord2(entry.file) ? entry.file : {};
|
|
14511
|
+
const objectInfo = isObjectRecord2(fileInfo.objectInfo) ? fileInfo.objectInfo : {};
|
|
14512
|
+
const storageKey = objectInfo.key;
|
|
14513
|
+
if (typeof signedUrl !== "string" || typeof storageKey !== "string") {
|
|
14514
|
+
throw new Error("Upload presign response is missing signedUrl/key.");
|
|
14515
|
+
}
|
|
14516
|
+
const putHeaders = { "Content-Type": mimeType };
|
|
14517
|
+
if (typeof objectInfo.cacheControl === "string") {
|
|
14518
|
+
putHeaders["Cache-Control"] = objectInfo.cacheControl;
|
|
14519
|
+
}
|
|
14520
|
+
if (isObjectRecord2(objectInfo.metadata)) {
|
|
14521
|
+
for (const [key, value] of Object.entries(objectInfo.metadata)) {
|
|
14522
|
+
if (typeof value === "string") putHeaders[`x-amz-meta-${key}`] = value;
|
|
14523
|
+
}
|
|
14524
|
+
}
|
|
14525
|
+
if (isObjectRecord2(entry.headers)) {
|
|
14526
|
+
for (const [key, value] of Object.entries(entry.headers)) {
|
|
14527
|
+
if (typeof value === "string") putHeaders[key] = value;
|
|
14528
|
+
}
|
|
14529
|
+
}
|
|
14530
|
+
const putResponse = await fetch(signedUrl, {
|
|
14531
|
+
method: "PUT",
|
|
14532
|
+
headers: putHeaders,
|
|
14533
|
+
body: new Uint8Array(bytes)
|
|
14534
|
+
});
|
|
14535
|
+
if (!putResponse.ok) {
|
|
14536
|
+
throw new Error(
|
|
14537
|
+
`Storage PUT failed (${putResponse.status}): ${await putResponse.text()}`
|
|
14538
|
+
);
|
|
14539
|
+
}
|
|
14540
|
+
const finalized = await context.client.post(
|
|
14541
|
+
versionPath(context.workspace, "files/finalize-upload"),
|
|
14542
|
+
{
|
|
14543
|
+
projectFileId,
|
|
14544
|
+
replaceFileId,
|
|
14545
|
+
name,
|
|
14546
|
+
mimeType,
|
|
14547
|
+
byteLength: bytes.byteLength,
|
|
14548
|
+
storageKey
|
|
14549
|
+
}
|
|
12645
14550
|
);
|
|
14551
|
+
console.log(JSON.stringify(finalized, null, 2));
|
|
12646
14552
|
}
|
|
12647
14553
|
async function runFiles(context, subcommand, positional) {
|
|
14554
|
+
if (subcommand === "upload") {
|
|
14555
|
+
const filePath = positional[0];
|
|
14556
|
+
if (filePath === void 0) {
|
|
14557
|
+
throw new Error(
|
|
14558
|
+
"Usage: neo files upload <path> [--template <templateId>] [--replace <fileId>]"
|
|
14559
|
+
);
|
|
14560
|
+
}
|
|
14561
|
+
const templateIndex = process.argv.indexOf("--template");
|
|
14562
|
+
const preferredTemplateId = templateIndex >= 0 ? process.argv[templateIndex + 1] ?? null : null;
|
|
14563
|
+
await uploadProjectFile(context, filePath, preferredTemplateId);
|
|
14564
|
+
return;
|
|
14565
|
+
}
|
|
12648
14566
|
if (subcommand === "list") {
|
|
12649
14567
|
const { raw } = await fetchProjectDocument(context.workspace);
|
|
12650
14568
|
const projectFiles = Array.isArray(raw.projectFiles) ? raw.projectFiles.filter(isObjectRecord2) : [];
|
|
@@ -12671,8 +14589,25 @@ async function runFiles(context, subcommand, positional) {
|
|
|
12671
14589
|
console.log(JSON.stringify(result, null, 2));
|
|
12672
14590
|
return;
|
|
12673
14591
|
}
|
|
14592
|
+
if (subcommand === "texture-settings") {
|
|
14593
|
+
const fileId = positional[0];
|
|
14594
|
+
const fileFlagIndex = process.argv.indexOf("--file");
|
|
14595
|
+
const payloadPath = fileFlagIndex >= 0 ? process.argv[fileFlagIndex + 1] ?? null : null;
|
|
14596
|
+
if (fileId === void 0 || payloadPath === null) {
|
|
14597
|
+
throw new Error(
|
|
14598
|
+
"Usage: neo files texture-settings <fileId> --file <settings.json>"
|
|
14599
|
+
);
|
|
14600
|
+
}
|
|
14601
|
+
const payload = JSON.parse(readFileSync10(payloadPath, "utf8"));
|
|
14602
|
+
const result = await context.client.post(
|
|
14603
|
+
versionPath(context.workspace, `files/${fileId}/unity-texture-settings`),
|
|
14604
|
+
payload
|
|
14605
|
+
);
|
|
14606
|
+
console.log(JSON.stringify(result, null, 2));
|
|
14607
|
+
return;
|
|
14608
|
+
}
|
|
12674
14609
|
throw new Error(
|
|
12675
|
-
`Unknown files subcommand "${subcommand}" (list | delete
|
|
14610
|
+
`Unknown files subcommand "${subcommand}" (upload | list | delete | texture-settings).`
|
|
12676
14611
|
);
|
|
12677
14612
|
}
|
|
12678
14613
|
var init_content = __esm({
|
|
@@ -12686,13 +14621,64 @@ var init_content = __esm({
|
|
|
12686
14621
|
}
|
|
12687
14622
|
});
|
|
12688
14623
|
|
|
14624
|
+
// src/commands/export.ts
|
|
14625
|
+
var export_exports = {};
|
|
14626
|
+
__export(export_exports, {
|
|
14627
|
+
runExportUnity: () => runExportUnity
|
|
14628
|
+
});
|
|
14629
|
+
import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync8 } from "node:fs";
|
|
14630
|
+
import { join as join10 } from "node:path";
|
|
14631
|
+
async function runExportUnity(workspace, outDir) {
|
|
14632
|
+
if (outDir === null) {
|
|
14633
|
+
throw new Error(
|
|
14634
|
+
"Usage: neo export unity --out <UnityAssetsDir> (the Unity project's Assets folder)"
|
|
14635
|
+
);
|
|
14636
|
+
}
|
|
14637
|
+
const client = NeoApiClient.forBaseUrl(workspace.config.apiBaseUrl);
|
|
14638
|
+
const response = await client.post(
|
|
14639
|
+
`/api/projects/${workspace.config.projectId}/export`,
|
|
14640
|
+
{ versionId: workspace.config.versionId }
|
|
14641
|
+
);
|
|
14642
|
+
const resourcesDir = join10(outDir, "Resources", "Neo");
|
|
14643
|
+
const localizationDir = join10(resourcesDir, "Localization");
|
|
14644
|
+
const scriptsDir = join10(outDir, "Scripts", "Neo");
|
|
14645
|
+
mkdirSync8(localizationDir, { recursive: true });
|
|
14646
|
+
mkdirSync8(scriptsDir, { recursive: true });
|
|
14647
|
+
writeFileSync8(join10(resourcesDir, "project.json"), response.projectJson);
|
|
14648
|
+
writeFileSync8(
|
|
14649
|
+
join10(scriptsDir, "NeoGeneratedTypes.cs"),
|
|
14650
|
+
response.generatedTypes
|
|
14651
|
+
);
|
|
14652
|
+
for (const file of response.localizationFiles ?? []) {
|
|
14653
|
+
writeFileSync8(join10(localizationDir, file.fileName), file.content);
|
|
14654
|
+
}
|
|
14655
|
+
console.log(`wrote ${join10(resourcesDir, "project.json")}`);
|
|
14656
|
+
console.log(`wrote ${join10(scriptsDir, "NeoGeneratedTypes.cs")}`);
|
|
14657
|
+
for (const file of response.localizationFiles ?? []) {
|
|
14658
|
+
console.log(`wrote ${join10(localizationDir, file.fileName)}`);
|
|
14659
|
+
}
|
|
14660
|
+
const diagnostics = response.diagnostics ?? [];
|
|
14661
|
+
for (const diagnostic of diagnostics) {
|
|
14662
|
+
console.log(`${diagnostic.severity}: ${diagnostic.message}`);
|
|
14663
|
+
}
|
|
14664
|
+
if (diagnostics.length === 0) {
|
|
14665
|
+
console.log("no export diagnostics");
|
|
14666
|
+
}
|
|
14667
|
+
}
|
|
14668
|
+
var init_export = __esm({
|
|
14669
|
+
"src/commands/export.ts"() {
|
|
14670
|
+
"use strict";
|
|
14671
|
+
init_http();
|
|
14672
|
+
}
|
|
14673
|
+
});
|
|
14674
|
+
|
|
12689
14675
|
// src/commands/dev.ts
|
|
12690
14676
|
var dev_exports = {};
|
|
12691
14677
|
__export(dev_exports, {
|
|
12692
14678
|
runDev: () => runDev
|
|
12693
14679
|
});
|
|
12694
14680
|
import { watch } from "node:fs";
|
|
12695
|
-
import { join as
|
|
14681
|
+
import { join as join11 } from "node:path";
|
|
12696
14682
|
import { emitKeypressEvents } from "node:readline";
|
|
12697
14683
|
import { ConvexClient } from "convex/browser";
|
|
12698
14684
|
function isSchemaSignal(value) {
|
|
@@ -12795,7 +14781,7 @@ async function runDev(workspace, options) {
|
|
|
12795
14781
|
};
|
|
12796
14782
|
for (const dir of ["Attributes", "Enums"]) {
|
|
12797
14783
|
try {
|
|
12798
|
-
watch(
|
|
14784
|
+
watch(join11(workspace.root, dir), { persistent: true }, onFileChange);
|
|
12799
14785
|
} catch {
|
|
12800
14786
|
}
|
|
12801
14787
|
}
|
|
@@ -12880,13 +14866,19 @@ var EDITOR_SCOPES = [
|
|
|
12880
14866
|
"project:localization:values:write",
|
|
12881
14867
|
"project:localization:export",
|
|
12882
14868
|
"project:localization:import",
|
|
12883
|
-
"project:release-channel:read"
|
|
14869
|
+
"project:release-channel:read",
|
|
14870
|
+
// `neo export unity` writes project.json + NeoGeneratedTypes.cs headlessly
|
|
14871
|
+
// (the escape hatch when game code references not-yet-generated members and
|
|
14872
|
+
// a broken compile blocks the in-editor sync).
|
|
14873
|
+
"unity:export",
|
|
14874
|
+
// Branch lifecycle (auto-archive after `neo merge`, branch archive/restore)
|
|
14875
|
+
// is editor work; releases stay gated behind the release profile.
|
|
14876
|
+
"project:version:archive",
|
|
14877
|
+
"project:version:restore"
|
|
12884
14878
|
];
|
|
12885
14879
|
var RELEASE_SCOPES = [
|
|
12886
14880
|
...EDITOR_SCOPES,
|
|
12887
14881
|
"project:version:status:write",
|
|
12888
|
-
"project:version:archive",
|
|
12889
|
-
"project:version:restore",
|
|
12890
14882
|
"project:release-channel:write",
|
|
12891
14883
|
"project:release-channel:publish"
|
|
12892
14884
|
];
|
|
@@ -13103,6 +15095,12 @@ namespace NeoCompose.Schema
|
|
|
13103
15095
|
public string? Name { get; set; }
|
|
13104
15096
|
public string? ExtendsId { get; set; }
|
|
13105
15097
|
public bool Locked { get; set; }
|
|
15098
|
+
/// <summary>
|
|
15099
|
+
/// Storage class override: "static", "save", or "session". Absent
|
|
15100
|
+
/// means the attribute inherits its placement parent's storage
|
|
15101
|
+
/// (specs/attribute-storage.md).
|
|
15102
|
+
/// </summary>
|
|
15103
|
+
public string? Storage { get; set; }
|
|
13106
15104
|
public string? DefaultJson { get; set; }
|
|
13107
15105
|
public string? ExtraJson { get; set; }
|
|
13108
15106
|
protected NeoSchemaMemberAttribute(string? id) { Id = id; }
|
|
@@ -13115,6 +15113,11 @@ namespace NeoCompose.Schema
|
|
|
13115
15113
|
public string? Name { get; set; }
|
|
13116
15114
|
public string? ExtendsTypeId { get; set; }
|
|
13117
15115
|
public bool Hidden { get; set; }
|
|
15116
|
+
/// <summary>
|
|
15117
|
+
/// Placement constraint: "static", "save", or "session". Absent
|
|
15118
|
+
/// means the type may be placed at any storage class.
|
|
15119
|
+
/// </summary>
|
|
15120
|
+
public string? AllowedStorage { get; set; }
|
|
13118
15121
|
public string? ExtraJson { get; set; }
|
|
13119
15122
|
public NeoCustomTypeAttribute(string? id = null) { Id = id; }
|
|
13120
15123
|
public string? Id { get; }
|
|
@@ -13172,6 +15175,30 @@ namespace NeoCompose.Schema
|
|
|
13172
15175
|
public NeoStringAttribute(string? id = null) : base(id) { }
|
|
13173
15176
|
}
|
|
13174
15177
|
|
|
15178
|
+
[AttributeUsage(AttributeTargets.Property)]
|
|
15179
|
+
public sealed class NeoVector2Attribute : NeoSchemaMemberAttribute
|
|
15180
|
+
{
|
|
15181
|
+
public NeoVector2Attribute(string? id = null) : base(id) { }
|
|
15182
|
+
}
|
|
15183
|
+
|
|
15184
|
+
[AttributeUsage(AttributeTargets.Property)]
|
|
15185
|
+
public sealed class NeoVector2IntAttribute : NeoSchemaMemberAttribute
|
|
15186
|
+
{
|
|
15187
|
+
public NeoVector2IntAttribute(string? id = null) : base(id) { }
|
|
15188
|
+
}
|
|
15189
|
+
|
|
15190
|
+
[AttributeUsage(AttributeTargets.Property)]
|
|
15191
|
+
public sealed class NeoVector3Attribute : NeoSchemaMemberAttribute
|
|
15192
|
+
{
|
|
15193
|
+
public NeoVector3Attribute(string? id = null) : base(id) { }
|
|
15194
|
+
}
|
|
15195
|
+
|
|
15196
|
+
[AttributeUsage(AttributeTargets.Property)]
|
|
15197
|
+
public sealed class NeoVector3IntAttribute : NeoSchemaMemberAttribute
|
|
15198
|
+
{
|
|
15199
|
+
public NeoVector3IntAttribute(string? id = null) : base(id) { }
|
|
15200
|
+
}
|
|
15201
|
+
|
|
13175
15202
|
[AttributeUsage(AttributeTargets.Property)]
|
|
13176
15203
|
public sealed class NeoDictionaryAttribute : NeoSchemaMemberAttribute
|
|
13177
15204
|
{
|
|
@@ -13266,6 +15293,10 @@ namespace NeoCompose.Schema
|
|
|
13266
15293
|
public sealed class NeoAudioValue { }
|
|
13267
15294
|
public sealed class NeoFunctionValue { }
|
|
13268
15295
|
public sealed class NeoLookupRef { }
|
|
15296
|
+
public sealed class NeoVector2 { }
|
|
15297
|
+
public sealed class NeoVector2Int { }
|
|
15298
|
+
public sealed class NeoVector3 { }
|
|
15299
|
+
public sealed class NeoVector3Int { }
|
|
13269
15300
|
}
|
|
13270
15301
|
`;
|
|
13271
15302
|
function scaffoldWorkspace(root) {
|
|
@@ -13502,7 +15533,7 @@ ${h("Branches & releases")}
|
|
|
13502
15533
|
channel ${d("list | create | edit | delete")}
|
|
13503
15534
|
|
|
13504
15535
|
${h("Content & scripts")}
|
|
13505
|
-
values ${d("list | get | set")} loc ${d("locales | list | set |
|
|
15536
|
+
values ${d("list | get | set")} loc ${d("locales | list | set | archive | restore | ...")}
|
|
13506
15537
|
dialogue ${d("list | show | node | group | priority-group")}
|
|
13507
15538
|
records ${d("get | query")} files ${d("list | delete")}
|
|
13508
15539
|
script ${d("check | eval | apply")} migrate ${d("new | list | check | run | prune")}
|
|
@@ -13681,12 +15712,22 @@ async function main() {
|
|
|
13681
15712
|
}
|
|
13682
15713
|
return;
|
|
13683
15714
|
}
|
|
15715
|
+
case "export": {
|
|
15716
|
+
const workspace = loadWorkspace(process.cwd());
|
|
15717
|
+
const sub = args.positional[0];
|
|
15718
|
+
if (sub !== "unity") {
|
|
15719
|
+
throw new Error("neo export requires a subcommand: unity.");
|
|
15720
|
+
}
|
|
15721
|
+
const { runExportUnity: runExportUnity2 } = await Promise.resolve().then(() => (init_export(), export_exports));
|
|
15722
|
+
await runExportUnity2(workspace, stringFlag(args, "out"));
|
|
15723
|
+
return;
|
|
15724
|
+
}
|
|
13684
15725
|
case "script": {
|
|
13685
15726
|
const workspace = loadWorkspace(process.cwd());
|
|
13686
15727
|
const sub = args.positional[0];
|
|
13687
|
-
if (sub !== "check" && sub !== "eval" && sub !== "apply") {
|
|
15728
|
+
if (sub !== "check" && sub !== "eval" && sub !== "apply" && sub !== "compile") {
|
|
13688
15729
|
throw new Error(
|
|
13689
|
-
"neo script requires a subcommand: check | eval | apply."
|
|
15730
|
+
"neo script requires a subcommand: check | eval | apply | compile."
|
|
13690
15731
|
);
|
|
13691
15732
|
}
|
|
13692
15733
|
const { runScript: runScript2 } = await Promise.resolve().then(() => (init_script(), script_exports));
|