@neocompose/cli 0.50.1 → 0.50.3
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/CHANGELOG.md +9 -0
- package/dist/neo.mjs +347 -55
- package/package.json +1 -1
- package/skills/neocompose-cli/SKILL.md +1 -1
- package/skills/neocompose-cli/references/cli-development.md +1 -1
- package/skills/neocompose-cli/references/commands-and-sync.md +8 -0
- package/skills/neocompose-cli/references/values-identities-and-references.md +7 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.50.3] - 2026-09-14
|
|
4
|
+
|
|
5
|
+
- Preserve packed siblings when preparing push source identity for new children in an existing value graph. Pending seeds now fill missing rows without replacing reconstructed stored rows.
|
|
6
|
+
- Resolve omitted fields on stored classes without construction records from their shared declaration defaults. CLI validation, NeoScript reads, and editor value cells now agree without adding per-instance overrides or inventing constructor provenance.
|
|
7
|
+
|
|
8
|
+
## [0.50.2] - 2026-09-12
|
|
9
|
+
|
|
10
|
+
- Export Unity C# as separate files named after their generated types. Keep stable identities in the manifest to preserve Unity GUIDs through renames. Preserve unchanged files and their Unity metadata, remove obsolete owned outputs, and share the integrity manifest with editor synchronization.
|
|
11
|
+
|
|
3
12
|
## [0.50.1] - 2026-09-12
|
|
4
13
|
|
|
5
14
|
- Reuse schema and symbol indexes while compiling a push, including migration bodies, and compile each changed member body once. Bulk scripted changes no longer rebuild the complete project for every body.
|
package/dist/neo.mjs
CHANGED
|
@@ -35365,7 +35365,7 @@ var init_login = __esm({
|
|
|
35365
35365
|
"project:localization:export",
|
|
35366
35366
|
"project:localization:import",
|
|
35367
35367
|
"project:release-channel:read",
|
|
35368
|
-
// `neo export unity` writes project.json +
|
|
35368
|
+
// `neo export unity` writes project.json + generated C# files headlessly
|
|
35369
35369
|
// (the escape hatch when game code references not-yet-generated members and
|
|
35370
35370
|
// a broken compile blocks the in-editor sync).
|
|
35371
35371
|
"unity:export",
|
|
@@ -53894,7 +53894,7 @@ function buildDefaultMemberValue(args) {
|
|
|
53894
53894
|
`Cannot create a value of missing class "${effectiveClassId}".`
|
|
53895
53895
|
);
|
|
53896
53896
|
}
|
|
53897
|
-
if (member.payload !== 1 /* Partial */ && typeof effectiveClass.requiredConstructorId === "string" && args.constructorRoot === void 0 && args.authoredRoot === void 0) {
|
|
53897
|
+
if (member.payload !== 1 /* Partial */ && typeof effectiveClass.requiredConstructorId === "string" && args.constructorRoot === void 0 && args.authoredRoot === void 0 && args.declarationRoot !== true) {
|
|
53898
53898
|
throw new Error(
|
|
53899
53899
|
`Class "${effectiveClass.name}" declares a required constructor; provide its constructor arguments.`
|
|
53900
53900
|
);
|
|
@@ -53967,6 +53967,9 @@ function buildDefaultMemberValue(args) {
|
|
|
53967
53967
|
anchorsParentClassPartition = true;
|
|
53968
53968
|
}
|
|
53969
53969
|
if (record4[schemaKey] !== void 0) continue;
|
|
53970
|
+
if (args.declarationRoot === true && !isLiteralValueContent(resolvedChildMember.defaultValue)) {
|
|
53971
|
+
continue;
|
|
53972
|
+
}
|
|
53970
53973
|
if (args.authoredRoot?.suppliedSchemaKeys.has(schemaKey) === true) {
|
|
53971
53974
|
continue;
|
|
53972
53975
|
}
|
|
@@ -81273,10 +81276,10 @@ function materializeMemberDefaultValue(args) {
|
|
|
81273
81276
|
args.readTracking?.recordUnattributableValueRead(
|
|
81274
81277
|
`declaration-owner-resolution:${args.envelope.id}`
|
|
81275
81278
|
);
|
|
81276
|
-
const { valuesById, rootOwners } = declarationInitializerContext(
|
|
81277
|
-
args.document
|
|
81278
|
-
);
|
|
81279
81279
|
const declarationArguments = (member, init, sourceValueId) => {
|
|
81280
|
+
const { valuesById, rootOwners } = declarationInitializerContext(
|
|
81281
|
+
args.document
|
|
81282
|
+
);
|
|
81280
81283
|
const ownerClass = typeof sourceValueId === "string" ? initializerOwnerContext(
|
|
81281
81284
|
args.document,
|
|
81282
81285
|
sourceValueId,
|
|
@@ -81308,6 +81311,7 @@ function materializeMemberDefaultValue(args) {
|
|
|
81308
81311
|
member: args.member,
|
|
81309
81312
|
createdValues,
|
|
81310
81313
|
storageKeyDeclarations,
|
|
81314
|
+
declarationRoot: args.declarationRoot,
|
|
81311
81315
|
...args.genericEnv === void 0 ? {} : { genericEnv: args.genericEnv },
|
|
81312
81316
|
// A declaration default may itself be init-backed one level down (P43 §2),
|
|
81313
81317
|
// and those nested initializers must evaluate against the same document.
|
|
@@ -81750,13 +81754,12 @@ function resolveVirtualInstanceGraph(args) {
|
|
|
81750
81754
|
const effective = {
|
|
81751
81755
|
...mergeExpandedAndMaterializedRow(indexed.expandedRow, materialized),
|
|
81752
81756
|
id: effectiveId,
|
|
81753
|
-
//
|
|
81754
|
-
//
|
|
81757
|
+
// Materialization has already resolved each child's storage partition.
|
|
81758
|
+
// Virtual rows inherit root timestamps; real rows retain their envelope.
|
|
81755
81759
|
...materialized === null ? {
|
|
81756
81760
|
projectId: args.instanceRoot.projectId,
|
|
81757
81761
|
createdAt: args.instanceRoot.createdAt,
|
|
81758
|
-
updatedAt: args.instanceRoot.updatedAt
|
|
81759
|
-
...args.instanceRoot.mapKey === void 0 ? {} : { mapKey: args.instanceRoot.mapKey }
|
|
81762
|
+
updatedAt: args.instanceRoot.updatedAt
|
|
81760
81763
|
} : {}
|
|
81761
81764
|
};
|
|
81762
81765
|
if (isMemberClassBase(indexed.member) && effective.value !== null) {
|
|
@@ -82036,13 +82039,26 @@ function resolvedStoredInstanceRows(args) {
|
|
|
82036
82039
|
const rows = new Map(document.values.map((row) => [row.id, row]));
|
|
82037
82040
|
const materializedIndex = buildVirtualInstanceMaterializedIndex({ document });
|
|
82038
82041
|
const queue = [{ root: args.instanceRoot, member: args.rootMember }];
|
|
82042
|
+
const stored = indexExpansion({
|
|
82043
|
+
document,
|
|
82044
|
+
instanceRoot: args.instanceRoot,
|
|
82045
|
+
rootMember: args.rootMember,
|
|
82046
|
+
expandedRoot: rows.get(args.instanceRoot.id) ?? args.instanceRoot,
|
|
82047
|
+
expandedRows: document.values
|
|
82048
|
+
});
|
|
82049
|
+
for (const node of stored.nodesByPath.values()) {
|
|
82050
|
+
if (node.expandedRow.id === args.instanceRoot.id) continue;
|
|
82051
|
+
if (!isMemberClassBase(node.member)) continue;
|
|
82052
|
+
if (!isStoredClassResolutionRoot(node.expandedRow)) continue;
|
|
82053
|
+
queue.push({ root: node.expandedRow, member: node.member });
|
|
82054
|
+
}
|
|
82039
82055
|
const resolvedRootIds = /* @__PURE__ */ new Set();
|
|
82040
82056
|
const resolvedFrameByValueId = /* @__PURE__ */ new Map();
|
|
82041
82057
|
while (queue.length > 0) {
|
|
82042
82058
|
const next = queue.shift();
|
|
82043
82059
|
if (next === void 0 || resolvedRootIds.has(next.root.id)) continue;
|
|
82044
82060
|
resolvedRootIds.add(next.root.id);
|
|
82045
|
-
const expanded =
|
|
82061
|
+
const expanded = expandStoredClassValue({
|
|
82046
82062
|
evaluateInitializer: args.evaluateInitializer,
|
|
82047
82063
|
document,
|
|
82048
82064
|
instanceRoot: next.root,
|
|
@@ -82089,6 +82105,63 @@ function resolvedStoredInstanceRows(args) {
|
|
|
82089
82105
|
}
|
|
82090
82106
|
return rows;
|
|
82091
82107
|
}
|
|
82108
|
+
function expandStoredClassValue(args) {
|
|
82109
|
+
if (isLiteralValueContent(args.instanceRoot) && isVirtualInstanceRootShape(args.instanceRoot)) {
|
|
82110
|
+
return expandStoredInstance(args);
|
|
82111
|
+
}
|
|
82112
|
+
const root = literalRow(args.instanceRoot);
|
|
82113
|
+
if (root.constructorArgs !== void 0) return expandStoredInstance(args);
|
|
82114
|
+
const member = resolveMember2(args.rootMember, args.document.members);
|
|
82115
|
+
if (!isMemberClassBase(member)) {
|
|
82116
|
+
throw new Error(
|
|
82117
|
+
`Stored default projection member "${memberIdOf(args.rootMember)}" is not Class-valued.`
|
|
82118
|
+
);
|
|
82119
|
+
}
|
|
82120
|
+
if (member.payload === 1 /* Partial */) {
|
|
82121
|
+
return { root, rows: [], pinnedRootSchemaKeys: /* @__PURE__ */ new Set() };
|
|
82122
|
+
}
|
|
82123
|
+
if (root.value === null) {
|
|
82124
|
+
return { root, rows: [], pinnedRootSchemaKeys: /* @__PURE__ */ new Set() };
|
|
82125
|
+
}
|
|
82126
|
+
const classId = root.classId ?? member.classId;
|
|
82127
|
+
args.readRecorder?.recordValueRead(root.id);
|
|
82128
|
+
args.readRecorder?.recordGlobalRead("declaration-default");
|
|
82129
|
+
const materialized = materializeMemberDefaultValue({
|
|
82130
|
+
evaluateInitializer: args.evaluateInitializer,
|
|
82131
|
+
document: args.document,
|
|
82132
|
+
member: closeClassArgumentsFromRowStamp(
|
|
82133
|
+
{
|
|
82134
|
+
...member,
|
|
82135
|
+
classId,
|
|
82136
|
+
defaultValue: derivesContentFromMemberDefault({
|
|
82137
|
+
member,
|
|
82138
|
+
instanceRoot: root,
|
|
82139
|
+
effectiveClassId: classId
|
|
82140
|
+
}) ? member.defaultValue : void 0
|
|
82141
|
+
},
|
|
82142
|
+
root
|
|
82143
|
+
),
|
|
82144
|
+
envelope: root,
|
|
82145
|
+
declarationRoot: true
|
|
82146
|
+
});
|
|
82147
|
+
return {
|
|
82148
|
+
root: { ...root, classId, value: materialized.root.value },
|
|
82149
|
+
rows: materialized.createdValues,
|
|
82150
|
+
pinnedRootSchemaKeys: /* @__PURE__ */ new Set()
|
|
82151
|
+
};
|
|
82152
|
+
}
|
|
82153
|
+
function isStoredClassResolutionRoot(value) {
|
|
82154
|
+
if (!isLiteralValueContent(value)) return false;
|
|
82155
|
+
if (isVirtualInstanceRootShape(value)) return true;
|
|
82156
|
+
return typeof value.classId === "string" && value.value !== null && typeof value.value === "object" && !Array.isArray(value.value);
|
|
82157
|
+
}
|
|
82158
|
+
function storedClassResolutionMember(member, row, members) {
|
|
82159
|
+
const resolved = resolveMember2(member, members);
|
|
82160
|
+
if (isMemberClassBase(resolved)) return resolved;
|
|
82161
|
+
if (resolved.kind !== 21 /* Generic */ || typeof row.classId !== "string")
|
|
82162
|
+
return null;
|
|
82163
|
+
return { ...resolved, kind: 7 /* Class */, classId: row.classId };
|
|
82164
|
+
}
|
|
82092
82165
|
function planVirtualOverrideWrite(args) {
|
|
82093
82166
|
const target = args.graph.locationsById.get(args.valueId);
|
|
82094
82167
|
if (target === void 0) {
|
|
@@ -83569,8 +83642,12 @@ function createHeadlessVirtualInstanceResolver(args) {
|
|
|
83569
83642
|
failedRootIds.add(rootId);
|
|
83570
83643
|
return /* @__PURE__ */ new Map();
|
|
83571
83644
|
}
|
|
83572
|
-
const member =
|
|
83573
|
-
|
|
83645
|
+
const member = storedClassResolutionMember(
|
|
83646
|
+
declaredMember,
|
|
83647
|
+
instanceRoot,
|
|
83648
|
+
document.members
|
|
83649
|
+
);
|
|
83650
|
+
if (member === null || !isLiteralValueContent(instanceRoot)) {
|
|
83574
83651
|
failedRootIds.add(rootId);
|
|
83575
83652
|
return /* @__PURE__ */ new Map();
|
|
83576
83653
|
}
|
|
@@ -83580,7 +83657,7 @@ function createHeadlessVirtualInstanceResolver(args) {
|
|
|
83580
83657
|
document: resolverDocument,
|
|
83581
83658
|
materializedRows: document.values
|
|
83582
83659
|
});
|
|
83583
|
-
const expanded =
|
|
83660
|
+
const expanded = expandStoredClassValue({
|
|
83584
83661
|
evaluateInitializer: args.evaluateInitializer,
|
|
83585
83662
|
document: resolverDocument,
|
|
83586
83663
|
instanceRoot,
|
|
@@ -83622,7 +83699,7 @@ function createHeadlessVirtualInstanceResolver(args) {
|
|
|
83622
83699
|
while (placement !== null) {
|
|
83623
83700
|
args.readRecorder?.recordValueRead(placement.valueId);
|
|
83624
83701
|
const raw = rawById.get(placement.valueId);
|
|
83625
|
-
if (raw !== void 0 && isLiteralValueContent(raw) &&
|
|
83702
|
+
if (raw !== void 0 && isLiteralValueContent(raw) && isStoredClassResolutionRoot(raw)) {
|
|
83626
83703
|
return [...expandRoot(placement.valueId, placement.memberId).values()];
|
|
83627
83704
|
}
|
|
83628
83705
|
placement = placement.parentValueId === null ? null : placements().get(placement.parentValueId) ?? null;
|
|
@@ -84032,6 +84109,12 @@ var init_materialize_values = __esm({
|
|
|
84032
84109
|
});
|
|
84033
84110
|
|
|
84034
84111
|
// ../src/runtime/virtual-instance-values.ts
|
|
84112
|
+
function expandStoredClassValue2(args) {
|
|
84113
|
+
return expandStoredClassValue({
|
|
84114
|
+
...args,
|
|
84115
|
+
evaluateInitializer: evaluateMemberInitializer
|
|
84116
|
+
});
|
|
84117
|
+
}
|
|
84035
84118
|
function expandStoredInstance2(args) {
|
|
84036
84119
|
return expandStoredInstance({
|
|
84037
84120
|
...args,
|
|
@@ -101776,6 +101859,7 @@ var init_project_version_static_value_writes = __esm({
|
|
|
101776
101859
|
dialogueIds = /* @__PURE__ */ new Set();
|
|
101777
101860
|
schemaIndex;
|
|
101778
101861
|
virtualValueIds;
|
|
101862
|
+
declarationMaterializedIndex = null;
|
|
101779
101863
|
stack = /* @__PURE__ */ new Set();
|
|
101780
101864
|
createdCollectionStampByValueId = /* @__PURE__ */ new Map();
|
|
101781
101865
|
/** Built once and reused by every nested construction-root proof. */
|
|
@@ -101902,11 +101986,51 @@ var init_project_version_static_value_writes = __esm({
|
|
|
101902
101986
|
args.genericEnv,
|
|
101903
101987
|
args.path
|
|
101904
101988
|
);
|
|
101905
|
-
this.
|
|
101989
|
+
const resolved = this.resolveDeclaredDefaults(
|
|
101990
|
+
member,
|
|
101991
|
+
value,
|
|
101992
|
+
args.genericEnv
|
|
101993
|
+
);
|
|
101994
|
+
this.validateSemanticNode({ ...args, member, value: resolved });
|
|
101906
101995
|
} finally {
|
|
101907
101996
|
this.stack.delete(value.id);
|
|
101908
101997
|
}
|
|
101909
101998
|
}
|
|
101999
|
+
resolveDeclaredDefaults(member, value, genericEnv) {
|
|
102000
|
+
const closed = substituteMember(member, genericEnv, this.document.members);
|
|
102001
|
+
if (!isMemberClassBase(closed)) return value;
|
|
102002
|
+
if (!isLiteralValueContent(value)) return value;
|
|
102003
|
+
if (value.value === null || typeof value.value !== "object" || Array.isArray(value.value))
|
|
102004
|
+
return value;
|
|
102005
|
+
if (value.instanceConstructorId !== void 0 || value.instanceVariantId != null)
|
|
102006
|
+
return value;
|
|
102007
|
+
if (this.virtualValueIds.has(value.id)) return value;
|
|
102008
|
+
const expanded = expandStoredClassValue2({
|
|
102009
|
+
document: this.merged().document,
|
|
102010
|
+
instanceRoot: value,
|
|
102011
|
+
rootMember: closed
|
|
102012
|
+
});
|
|
102013
|
+
const graph = resolveVirtualInstanceGraph({
|
|
102014
|
+
document: this.merged().document,
|
|
102015
|
+
instanceRoot: value,
|
|
102016
|
+
rootMember: closed,
|
|
102017
|
+
expandedRoot: expanded.root,
|
|
102018
|
+
expandedRows: expanded.rows,
|
|
102019
|
+
materializedIndex: this.declarationMaterializedIndex ??= buildVirtualInstanceMaterializedIndex({
|
|
102020
|
+
document: this.merged().document
|
|
102021
|
+
})
|
|
102022
|
+
});
|
|
102023
|
+
for (const [id2, row] of graph.rowsById) {
|
|
102024
|
+
if (this.valueById.has(id2)) continue;
|
|
102025
|
+
this.valueById.set(id2, row);
|
|
102026
|
+
}
|
|
102027
|
+
const resolved = graph.rowsById.get(value.id);
|
|
102028
|
+
if (resolved === void 0 || !isLiteralValueContent(resolved))
|
|
102029
|
+
return value;
|
|
102030
|
+
if (resolved.value === null || typeof resolved.value !== "object" || Array.isArray(resolved.value))
|
|
102031
|
+
return value;
|
|
102032
|
+
return { ...resolved, value: { ...resolved.value, ...value.value } };
|
|
102033
|
+
}
|
|
101910
102034
|
/**
|
|
101911
102035
|
* A static graph can contain nested construction roots under an ordinary
|
|
101912
102036
|
* list or Class root. Replay each reachable root that carries complete
|
|
@@ -130834,20 +130958,22 @@ function createPendingProjectSourceIdentityV4(workspace, status, authoredValueSe
|
|
|
130834
130958
|
});
|
|
130835
130959
|
}
|
|
130836
130960
|
if (seed.valueId === void 0) continue;
|
|
130837
|
-
records2.
|
|
130838
|
-
|
|
130839
|
-
|
|
130840
|
-
|
|
130841
|
-
|
|
130842
|
-
|
|
130843
|
-
|
|
130844
|
-
|
|
130845
|
-
|
|
130846
|
-
|
|
130847
|
-
|
|
130848
|
-
|
|
130849
|
-
|
|
130961
|
+
if (!records2.has(`value:${seed.valueId}`))
|
|
130962
|
+
records2.set(`value:${seed.valueId}`, {
|
|
130963
|
+
recordKind: "value",
|
|
130964
|
+
recordId: seed.valueId,
|
|
130965
|
+
contentHash: "",
|
|
130966
|
+
deleted: false,
|
|
130967
|
+
data: {
|
|
130968
|
+
id: seed.valueId,
|
|
130969
|
+
projectId: workspace.config.projectId,
|
|
130970
|
+
...encodeStoredValueNode(seed),
|
|
130971
|
+
createdAt: now,
|
|
130972
|
+
updatedAt: now
|
|
130973
|
+
}
|
|
130974
|
+
});
|
|
130850
130975
|
for (const value of seed.values ?? []) {
|
|
130976
|
+
if (records2.has(`value:${value.id}`)) continue;
|
|
130851
130977
|
records2.set(`value:${value.id}`, {
|
|
130852
130978
|
recordKind: "value",
|
|
130853
130979
|
recordId: value.id,
|
|
@@ -132352,7 +132478,7 @@ var init_registry2 = __esm({
|
|
|
132352
132478
|
"schema-contract/registry.mjs"() {
|
|
132353
132479
|
"use strict";
|
|
132354
132480
|
PROJECT_SCHEMA_CONTRACT = Object.freeze({
|
|
132355
|
-
cliVersion: "0.50.
|
|
132481
|
+
cliVersion: "0.50.3",
|
|
132356
132482
|
projectFileUploadBatchSize: 32,
|
|
132357
132483
|
documentRecords: {
|
|
132358
132484
|
member: {
|
|
@@ -138828,13 +138954,177 @@ var init_dialogue_dryrun = __esm({
|
|
|
138828
138954
|
}
|
|
138829
138955
|
});
|
|
138830
138956
|
|
|
138957
|
+
// src/unity-generated-files.ts
|
|
138958
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
138959
|
+
import {
|
|
138960
|
+
existsSync as existsSync16,
|
|
138961
|
+
mkdirSync as mkdirSync13,
|
|
138962
|
+
readFileSync as readFileSync21,
|
|
138963
|
+
unlinkSync,
|
|
138964
|
+
writeFileSync as writeFileSync13
|
|
138965
|
+
} from "node:fs";
|
|
138966
|
+
import { dirname as dirname11, join as join18 } from "node:path";
|
|
138967
|
+
function validatePath(path) {
|
|
138968
|
+
if (!/^Generated\/[a-zA-Z0-9_%./-]+\.g\.cs$/.test(path))
|
|
138969
|
+
throw new Error(`Invalid generated file path: ${path}`);
|
|
138970
|
+
if (path.split("/").some((part) => part === "." || part === ".." || part === ""))
|
|
138971
|
+
throw new Error(`Invalid generated file path segment: ${path}`);
|
|
138972
|
+
}
|
|
138973
|
+
function readManifest(path) {
|
|
138974
|
+
if (!existsSync16(path)) return null;
|
|
138975
|
+
const data = JSON.parse(readFileSync21(path, "utf8"));
|
|
138976
|
+
if (data === null || typeof data !== "object" || !("schemaVersion" in data) || data.schemaVersion !== 1)
|
|
138977
|
+
throw new Error(`Invalid generated-file manifest version: ${path}`);
|
|
138978
|
+
if (!("files" in data) || !Array.isArray(data.files) || data.files.length === 0)
|
|
138979
|
+
throw new Error(`Invalid generated-file manifest entries: ${path}`);
|
|
138980
|
+
if (!("projectId" in data) || typeof data.projectId !== "string")
|
|
138981
|
+
throw new Error(`Invalid generated-file manifest project: ${path}`);
|
|
138982
|
+
const ids = /* @__PURE__ */ new Set();
|
|
138983
|
+
const paths = /* @__PURE__ */ new Set();
|
|
138984
|
+
const files = data.files.map((entry) => {
|
|
138985
|
+
if (entry === null || typeof entry !== "object" || !("path" in entry) || typeof entry.path !== "string")
|
|
138986
|
+
throw new Error(`Invalid generated-file manifest entry: ${path}`);
|
|
138987
|
+
if (!("id" in entry) || typeof entry.id !== "string" || entry.id.length === 0)
|
|
138988
|
+
throw new Error(`Invalid generated-file manifest identity: ${path}`);
|
|
138989
|
+
if (ids.has(entry.id))
|
|
138990
|
+
throw new Error(
|
|
138991
|
+
`Duplicate generated-file manifest identity: ${entry.id}`
|
|
138992
|
+
);
|
|
138993
|
+
ids.add(entry.id);
|
|
138994
|
+
validatePath(entry.path);
|
|
138995
|
+
if (paths.has(entry.path.toLowerCase()))
|
|
138996
|
+
throw new Error(`Duplicate generated-file manifest path: ${entry.path}`);
|
|
138997
|
+
paths.add(entry.path.toLowerCase());
|
|
138998
|
+
return { id: entry.id, path: entry.path };
|
|
138999
|
+
});
|
|
139000
|
+
return { projectId: data.projectId, files };
|
|
139001
|
+
}
|
|
139002
|
+
function writeUnityGeneratedFiles(directory, projectId, files) {
|
|
139003
|
+
if (!Array.isArray(files) || files.length === 0)
|
|
139004
|
+
throw new Error(
|
|
139005
|
+
"The export returned no generated files. Update the server before exporting Unity code."
|
|
139006
|
+
);
|
|
139007
|
+
const expected = /* @__PURE__ */ new Set();
|
|
139008
|
+
const ids = /* @__PURE__ */ new Set();
|
|
139009
|
+
for (const file of files) {
|
|
139010
|
+
validatePath(file.path);
|
|
139011
|
+
if (typeof file.id !== "string" || file.id.length === 0)
|
|
139012
|
+
throw new Error(`Missing generated file identity: ${file.path}`);
|
|
139013
|
+
if (ids.has(file.id))
|
|
139014
|
+
throw new Error(`Duplicate generated file identity: ${file.id}`);
|
|
139015
|
+
ids.add(file.id);
|
|
139016
|
+
const key = file.path.toLowerCase();
|
|
139017
|
+
if (expected.has(key))
|
|
139018
|
+
throw new Error(`Duplicate generated file path: ${file.path}`);
|
|
139019
|
+
expected.add(key);
|
|
139020
|
+
if (typeof file.content !== "string" || file.content.trim().length === 0)
|
|
139021
|
+
throw new Error(`Empty generated C# file: ${file.path}`);
|
|
139022
|
+
}
|
|
139023
|
+
const manifestPath = join18(directory, "NeoGeneratedFiles.json");
|
|
139024
|
+
const manifest = readManifest(manifestPath);
|
|
139025
|
+
const previousFiles = manifest?.files ?? [];
|
|
139026
|
+
const previousById = new Map(previousFiles.map((file) => [file.id, file]));
|
|
139027
|
+
const previousPaths = new Set(
|
|
139028
|
+
previousFiles.map((file) => file.path.toLowerCase())
|
|
139029
|
+
);
|
|
139030
|
+
const incomingById = new Map(files.map((file) => [file.id, file]));
|
|
139031
|
+
const operations = [];
|
|
139032
|
+
for (const file of previousFiles) {
|
|
139033
|
+
if (incomingById.get(file.id)?.path === file.path && manifest?.projectId === projectId)
|
|
139034
|
+
continue;
|
|
139035
|
+
operations.push(
|
|
139036
|
+
[join18(directory, file.path), null],
|
|
139037
|
+
[join18(directory, file.path + ".meta"), null]
|
|
139038
|
+
);
|
|
139039
|
+
}
|
|
139040
|
+
operations.push(
|
|
139041
|
+
[join18(directory, "NeoGeneratedTypes.cs"), null],
|
|
139042
|
+
[join18(directory, "NeoGeneratedTypes.cs.meta"), null]
|
|
139043
|
+
);
|
|
139044
|
+
for (const file of files) {
|
|
139045
|
+
const old = manifest?.projectId === projectId ? previousById.get(file.id) : void 0;
|
|
139046
|
+
const path = join18(directory, file.path);
|
|
139047
|
+
if (old !== void 0 && old.path !== file.path) {
|
|
139048
|
+
if (!previousPaths.has(file.path.toLowerCase()) && (existsSync16(path) || existsSync16(path + ".meta")))
|
|
139049
|
+
throw new Error(
|
|
139050
|
+
`Generated rename destination is not owned by the manifest: ${file.path}`
|
|
139051
|
+
);
|
|
139052
|
+
const meta = join18(directory, old.path + ".meta");
|
|
139053
|
+
operations.push([
|
|
139054
|
+
path + ".meta",
|
|
139055
|
+
existsSync16(meta) ? readFileSync21(meta, "utf8") : null
|
|
139056
|
+
]);
|
|
139057
|
+
}
|
|
139058
|
+
operations.push([path, file.content]);
|
|
139059
|
+
}
|
|
139060
|
+
operations.push([
|
|
139061
|
+
manifestPath,
|
|
139062
|
+
JSON.stringify(
|
|
139063
|
+
{
|
|
139064
|
+
schemaVersion: 1,
|
|
139065
|
+
projectId,
|
|
139066
|
+
files: [...files].sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0).map((file) => ({
|
|
139067
|
+
id: file.id,
|
|
139068
|
+
path: file.path,
|
|
139069
|
+
hash: createHash12("sha256").update(file.content).digest("hex")
|
|
139070
|
+
}))
|
|
139071
|
+
},
|
|
139072
|
+
null,
|
|
139073
|
+
2
|
|
139074
|
+
) + "\n"
|
|
139075
|
+
]);
|
|
139076
|
+
const previous = /* @__PURE__ */ new Map();
|
|
139077
|
+
for (const [path] of operations) {
|
|
139078
|
+
const key = path.toLowerCase();
|
|
139079
|
+
if (!previous.has(key))
|
|
139080
|
+
previous.set(key, {
|
|
139081
|
+
path,
|
|
139082
|
+
content: existsSync16(path) ? readFileSync21(path, "utf8") : null
|
|
139083
|
+
});
|
|
139084
|
+
}
|
|
139085
|
+
const write = (path, content) => {
|
|
139086
|
+
if (content === null) {
|
|
139087
|
+
if (existsSync16(path)) unlinkSync(path);
|
|
139088
|
+
} else {
|
|
139089
|
+
mkdirSync13(dirname11(path), { recursive: true });
|
|
139090
|
+
writeFileSync13(path, content);
|
|
139091
|
+
}
|
|
139092
|
+
};
|
|
139093
|
+
const changed = [];
|
|
139094
|
+
try {
|
|
139095
|
+
for (const [path, content] of operations) {
|
|
139096
|
+
const current = existsSync16(path) ? readFileSync21(path, "utf8") : null;
|
|
139097
|
+
if (current === content) continue;
|
|
139098
|
+
changed.push(path);
|
|
139099
|
+
write(path, content);
|
|
139100
|
+
}
|
|
139101
|
+
} catch (error) {
|
|
139102
|
+
const restored = /* @__PURE__ */ new Set();
|
|
139103
|
+
for (const path of changed.reverse()) {
|
|
139104
|
+
const key = path.toLowerCase();
|
|
139105
|
+
if (restored.has(key)) continue;
|
|
139106
|
+
restored.add(key);
|
|
139107
|
+
const original = previous.get(key);
|
|
139108
|
+
if (original.path !== path && existsSync16(path)) unlinkSync(path);
|
|
139109
|
+
const current = existsSync16(original.path) ? readFileSync21(original.path, "utf8") : null;
|
|
139110
|
+
if (current !== original.content) write(original.path, original.content);
|
|
139111
|
+
}
|
|
139112
|
+
throw error;
|
|
139113
|
+
}
|
|
139114
|
+
}
|
|
139115
|
+
var init_unity_generated_files = __esm({
|
|
139116
|
+
"src/unity-generated-files.ts"() {
|
|
139117
|
+
"use strict";
|
|
139118
|
+
}
|
|
139119
|
+
});
|
|
139120
|
+
|
|
138831
139121
|
// src/commands/export.ts
|
|
138832
139122
|
var export_exports = {};
|
|
138833
139123
|
__export(export_exports, {
|
|
138834
139124
|
runExportUnity: () => runExportUnity
|
|
138835
139125
|
});
|
|
138836
|
-
import { mkdirSync as
|
|
138837
|
-
import { join as
|
|
139126
|
+
import { mkdirSync as mkdirSync14, writeFileSync as writeFileSync14 } from "node:fs";
|
|
139127
|
+
import { join as join19 } from "node:path";
|
|
138838
139128
|
async function runExportUnity(workspace, outDir) {
|
|
138839
139129
|
if (outDir === null) {
|
|
138840
139130
|
throw new Error(
|
|
@@ -138846,23 +139136,24 @@ async function runExportUnity(workspace, outDir) {
|
|
|
138846
139136
|
`/api/projects/${workspace.config.projectId}/export`,
|
|
138847
139137
|
{ versionId: workspace.config.versionId }
|
|
138848
139138
|
);
|
|
138849
|
-
const resourcesDir =
|
|
138850
|
-
const localizationDir =
|
|
138851
|
-
const scriptsDir =
|
|
138852
|
-
|
|
138853
|
-
|
|
138854
|
-
|
|
138855
|
-
|
|
138856
|
-
|
|
138857
|
-
response.
|
|
138858
|
-
);
|
|
139139
|
+
const resourcesDir = join19(outDir, "Resources", "Neo");
|
|
139140
|
+
const localizationDir = join19(resourcesDir, "Localization");
|
|
139141
|
+
const scriptsDir = join19(outDir, "Scripts", "Neo");
|
|
139142
|
+
mkdirSync14(localizationDir, { recursive: true });
|
|
139143
|
+
mkdirSync14(scriptsDir, { recursive: true });
|
|
139144
|
+
writeUnityGeneratedFiles(
|
|
139145
|
+
scriptsDir,
|
|
139146
|
+
response.projectId,
|
|
139147
|
+
response.generatedFiles
|
|
139148
|
+
);
|
|
139149
|
+
writeFileSync14(join19(resourcesDir, "project.json"), response.projectJson);
|
|
138859
139150
|
for (const file of response.localizationFiles ?? []) {
|
|
138860
|
-
|
|
139151
|
+
writeFileSync14(join19(localizationDir, file.fileName), file.content);
|
|
138861
139152
|
}
|
|
138862
|
-
console.log(`wrote ${
|
|
138863
|
-
console.log(`
|
|
139153
|
+
console.log(`wrote ${join19(resourcesDir, "project.json")}`);
|
|
139154
|
+
console.log(`synchronized generated files in ${scriptsDir}`);
|
|
138864
139155
|
for (const file of response.localizationFiles ?? []) {
|
|
138865
|
-
console.log(`wrote ${
|
|
139156
|
+
console.log(`wrote ${join19(localizationDir, file.fileName)}`);
|
|
138866
139157
|
}
|
|
138867
139158
|
const diagnostics = response.diagnostics ?? [];
|
|
138868
139159
|
for (const diagnostic of diagnostics) {
|
|
@@ -138875,6 +139166,7 @@ async function runExportUnity(workspace, outDir) {
|
|
|
138875
139166
|
var init_export = __esm({
|
|
138876
139167
|
"src/commands/export.ts"() {
|
|
138877
139168
|
"use strict";
|
|
139169
|
+
init_unity_generated_files();
|
|
138878
139170
|
init_http();
|
|
138879
139171
|
}
|
|
138880
139172
|
});
|
|
@@ -138885,7 +139177,7 @@ __export(dev_exports, {
|
|
|
138885
139177
|
runDev: () => runDev
|
|
138886
139178
|
});
|
|
138887
139179
|
import { watch } from "node:fs";
|
|
138888
|
-
import { join as
|
|
139180
|
+
import { join as join20 } from "node:path";
|
|
138889
139181
|
import { emitKeypressEvents } from "node:readline";
|
|
138890
139182
|
import { ConvexClient } from "convex/browser";
|
|
138891
139183
|
function isSchemaSignal(value) {
|
|
@@ -138995,7 +139287,7 @@ async function runDev(workspace, options) {
|
|
|
138995
139287
|
};
|
|
138996
139288
|
for (const dir of ["Classes", "Enums"]) {
|
|
138997
139289
|
try {
|
|
138998
|
-
watch(
|
|
139290
|
+
watch(join20(workspace.root, dir), { persistent: true }, onFileChange);
|
|
138999
139291
|
} catch {
|
|
139000
139292
|
}
|
|
139001
139293
|
}
|
|
@@ -139049,8 +139341,8 @@ __export(resolve_exports, {
|
|
|
139049
139341
|
resolveMarkers: () => resolveMarkers,
|
|
139050
139342
|
runResolve: () => runResolve
|
|
139051
139343
|
});
|
|
139052
|
-
import { readFileSync as
|
|
139053
|
-
import { join as
|
|
139344
|
+
import { readFileSync as readFileSync22, rmSync as rmSync8, writeFileSync as writeFileSync15 } from "node:fs";
|
|
139345
|
+
import { join as join21, relative as relative11 } from "node:path";
|
|
139054
139346
|
function runResolve(workspace, side) {
|
|
139055
139347
|
const marked = readMarkedProjectSourcesV4(workspace.root);
|
|
139056
139348
|
assertRecordConflictsResolveTogetherV4(
|
|
@@ -139060,7 +139352,7 @@ function runResolve(workspace, side) {
|
|
|
139060
139352
|
const resolvedRecords = adoptServerConflictBases(workspace);
|
|
139061
139353
|
let resolvedFiles = 0;
|
|
139062
139354
|
for (const file of marked) {
|
|
139063
|
-
|
|
139355
|
+
writeFileSync15(file.absolutePath, resolveMarkers(file.source, side), "utf8");
|
|
139064
139356
|
resolvedFiles += 1;
|
|
139065
139357
|
}
|
|
139066
139358
|
let resolvedBinaries = 0;
|
|
@@ -139068,12 +139360,12 @@ function runResolve(workspace, side) {
|
|
|
139068
139360
|
const binary = state.projectBinary;
|
|
139069
139361
|
const conflict2 = binary?.conflict;
|
|
139070
139362
|
if (binary === void 0 || conflict2 === void 0) continue;
|
|
139071
|
-
const destination =
|
|
139363
|
+
const destination = join21(workspace.root, binary.path);
|
|
139072
139364
|
if (side === "theirs") {
|
|
139073
139365
|
if (conflict2.remoteSha256 !== null && conflict2.artifactPath !== void 0) {
|
|
139074
139366
|
writeVerifiedBinaryDownloadV4(
|
|
139075
139367
|
destination,
|
|
139076
|
-
|
|
139368
|
+
readFileSync22(join21(workspace.root, conflict2.artifactPath)),
|
|
139077
139369
|
conflict2.remoteSha256
|
|
139078
139370
|
);
|
|
139079
139371
|
binary.sha256 = conflict2.remoteSha256;
|
|
@@ -139083,7 +139375,7 @@ function runResolve(workspace, side) {
|
|
|
139083
139375
|
}
|
|
139084
139376
|
}
|
|
139085
139377
|
if (conflict2.artifactPath !== void 0) {
|
|
139086
|
-
rmSync8(
|
|
139378
|
+
rmSync8(join21(workspace.root, conflict2.artifactPath), { force: true });
|
|
139087
139379
|
}
|
|
139088
139380
|
delete binary.conflict;
|
|
139089
139381
|
resolvedBinaries += 1;
|
|
@@ -139102,7 +139394,7 @@ function runResolve(workspace, side) {
|
|
|
139102
139394
|
function readMarkedProjectSourcesV4(root) {
|
|
139103
139395
|
const files = [];
|
|
139104
139396
|
for (const absolutePath of listProjectSourceFilesV4(root)) {
|
|
139105
|
-
const source =
|
|
139397
|
+
const source = readFileSync22(absolutePath, "utf8");
|
|
139106
139398
|
if (detectConflictMarkers(source) === null) continue;
|
|
139107
139399
|
files.push({
|
|
139108
139400
|
path: normalizeWorkspaceSourcePath(relative11(root, absolutePath)),
|
|
@@ -139402,7 +139694,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
|
|
|
139402
139694
|
async function main() {
|
|
139403
139695
|
const args = parseArgs(process.argv.slice(2));
|
|
139404
139696
|
if (args.command === "--version") {
|
|
139405
|
-
console.log("0.50.
|
|
139697
|
+
console.log("0.50.3");
|
|
139406
139698
|
return;
|
|
139407
139699
|
}
|
|
139408
139700
|
if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
|
package/package.json
CHANGED
|
@@ -88,7 +88,7 @@ wrappers.
|
|
|
88
88
|
The marker near the top of `SKILL.md` must exactly match the package version:
|
|
89
89
|
|
|
90
90
|
```html
|
|
91
|
-
<!-- reviewed-through-cli: 0.50.
|
|
91
|
+
<!-- reviewed-through-cli: 0.50.3 -->
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
The quoted version above is checked too, so this instruction cannot go stale
|
|
@@ -246,3 +246,11 @@ honor both variables.
|
|
|
246
246
|
|
|
247
247
|
Pass explicit project/version IDs and flags in automation. Prefer `--json` for
|
|
248
248
|
machine-readable output and do not depend on interactive pickers or confirms.
|
|
249
|
+
|
|
250
|
+
## Unity code export
|
|
251
|
+
|
|
252
|
+
`neo export unity --out <UnityAssetsDir>` writes separate C# files under
|
|
253
|
+
`Scripts/Neo/Generated/`, named after the generated C# types, plus project and localization JSON. It compares
|
|
254
|
+
contents before writing C#, preserves unchanged `.meta` files, removes obsolete
|
|
255
|
+
outputs listed in `NeoGeneratedFiles.json`, and removes the former
|
|
256
|
+
`NeoGeneratedTypes.cs`. Stable identities in the manifest preserve `.meta` GUIDs when types are renamed. Keep the manifest with the generated files.
|
|
@@ -136,6 +136,13 @@ member creates a sparse override with its deterministic projected ID; removing
|
|
|
136
136
|
that member from an existing initializer resets the override and resumes source
|
|
137
137
|
tracking. Require either transition to appear in `neo diff`.
|
|
138
138
|
|
|
139
|
+
A stored class with no construction record still inherits literal declaration
|
|
140
|
+
defaults for omitted fields. For example, `FoodInfo.Quality = .OneStar` supplies
|
|
141
|
+
the quality of an existing food row that stores only its price. Do not add an
|
|
142
|
+
instance override to repair that omission. Reads do not invent a historical
|
|
143
|
+
constructor, and required fields with no applicable default remain errors.
|
|
144
|
+
Explicit stored overrides and partial payloads keep their existing meaning.
|
|
145
|
+
|
|
139
146
|
Generic arguments specify types, not default values. A required generic member
|
|
140
147
|
must be supplied at construction unless its member declaration or an inherited
|
|
141
148
|
member declaration supplies a default. Missing required members are compile
|