@neocompose/cli 0.69.0 → 0.69.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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.69.1] - 2026-10-06
|
|
4
|
+
|
|
5
|
+
- `neo export` shows a spinner while it works, labeled with the current step: reading the pulled project, building the export, generating Unity types, writing changed files, and checking local changes.
|
|
6
|
+
- `neo push` no longer carries a replaced class member's stored value into a new member with the same name. The old member's value rows are deleted and the new member's value is seeded fresh. Before this fix, push could fail with errors such as "single-select lookup cannot store multiple targets".
|
|
7
|
+
- `neo push --dry-run` can replay a stored construction that sets a `NeoAction` member, for example `OnApply = []` on a watcher stored inside an item stack. Before this fix, the dry run's round-trip check failed with "Cannot infer the entry type of an empty list".
|
|
8
|
+
|
|
3
9
|
## [0.69.0] - 2026-10-05
|
|
4
10
|
|
|
5
11
|
- **Breaking:** `neo export` replaces `neo export unity --out <dir>`. It builds the Unity export from the last pull, offline, and writes it into the directories the Unity project's `NeoComposeConfig` asset names. It writes only files whose bytes changed, skips codegen when only values changed since the last export into that Unity project, and writes `Library/NeoCompose/export.json` last for Unity to ingest. Local edits aren't exported, and the output counts them. An error diagnostic, or a project file whose bytes differ from the pulled file, stops the export before anything is written.
|
package/dist/neo.mjs
CHANGED
|
@@ -17687,6 +17687,17 @@ var init_strict_resolver = __esm({
|
|
|
17687
17687
|
targetType,
|
|
17688
17688
|
schemaClass2
|
|
17689
17689
|
);
|
|
17690
|
+
if (memberType2.kind === "action") {
|
|
17691
|
+
return {
|
|
17692
|
+
schemaKey: member.schemaKey ?? member.name,
|
|
17693
|
+
memberId: member.id,
|
|
17694
|
+
valuePointer: this.resolveReplayedListenerSet(
|
|
17695
|
+
entry.value,
|
|
17696
|
+
memberType2,
|
|
17697
|
+
scope
|
|
17698
|
+
)
|
|
17699
|
+
};
|
|
17700
|
+
}
|
|
17690
17701
|
const value = this.resolveExpression(entry.value, scope, memberType2);
|
|
17691
17702
|
if (!isNeoScriptTypeAssignable(value.type, memberType2, this.project)) {
|
|
17692
17703
|
throw new CompileError(
|
|
@@ -17701,6 +17712,43 @@ var init_strict_resolver = __esm({
|
|
|
17701
17712
|
};
|
|
17702
17713
|
});
|
|
17703
17714
|
}
|
|
17715
|
+
/**
|
|
17716
|
+
* P62 §2. A stored action slot replays as the listener list it stores. Each
|
|
17717
|
+
* entry resolves to the same static (member, row) identity the authored list
|
|
17718
|
+
* lowers to. Executable script cannot reassign a listener set (§3.2).
|
|
17719
|
+
*/
|
|
17720
|
+
resolveReplayedListenerSet(expression, actionType, scope) {
|
|
17721
|
+
if (this.context.storedConstructionReplay !== true) {
|
|
17722
|
+
throw new CompileError(
|
|
17723
|
+
`Cannot assign to ${this.describe(actionType)}; a listener set is not reassignable from script. Use '+=' to add a listener or '-=' to remove one.`,
|
|
17724
|
+
expression.pos
|
|
17725
|
+
);
|
|
17726
|
+
}
|
|
17727
|
+
if (expression.kind !== "litList") {
|
|
17728
|
+
throw new CompileError(
|
|
17729
|
+
`A stored ${this.describe(actionType)} replays as a bracketed listener list.`,
|
|
17730
|
+
expression.pos
|
|
17731
|
+
);
|
|
17732
|
+
}
|
|
17733
|
+
const listeners = [];
|
|
17734
|
+
for (const element of expression.elements) {
|
|
17735
|
+
const listener = this.resolveActionListener(element, actionType, scope);
|
|
17736
|
+
if (listener.type !== "value" /* Value */) {
|
|
17737
|
+
throw new CompileError(
|
|
17738
|
+
"A stored listener must name a stable (member, row) target.",
|
|
17739
|
+
element.pos
|
|
17740
|
+
);
|
|
17741
|
+
}
|
|
17742
|
+
listeners.push(listener.value.value);
|
|
17743
|
+
}
|
|
17744
|
+
return {
|
|
17745
|
+
type: "value" /* Value */,
|
|
17746
|
+
value: {
|
|
17747
|
+
typeInfo: toWireType(actionType, this.project),
|
|
17748
|
+
value: { listeners }
|
|
17749
|
+
}
|
|
17750
|
+
};
|
|
17751
|
+
}
|
|
17704
17752
|
resolveTileConversion(callee, args, typeArguments, scope, pos) {
|
|
17705
17753
|
const receiver = this.resolveExpression(callee.receiver, scope);
|
|
17706
17754
|
const owner = receiver.type.kind === "named" ? this.project.typeById.get(receiver.type.typeId) : void 0;
|
|
@@ -31763,7 +31811,7 @@ var init_registry = __esm({
|
|
|
31763
31811
|
"../packages/manifest/schema-contract/registry.mjs"() {
|
|
31764
31812
|
"use strict";
|
|
31765
31813
|
PROJECT_SCHEMA_CONTRACT = Object.freeze({
|
|
31766
|
-
cliVersion: "0.69.
|
|
31814
|
+
cliVersion: "0.69.1",
|
|
31767
31815
|
projectFileUploadBatchSize: 32,
|
|
31768
31816
|
documentRecords: {
|
|
31769
31817
|
member: {
|
|
@@ -90621,7 +90669,6 @@ var project_document_read_exports = {};
|
|
|
90621
90669
|
__export(project_document_read_exports, {
|
|
90622
90670
|
DOCUMENT_MANIFEST_PAGE_SIZE: () => DOCUMENT_MANIFEST_PAGE_SIZE,
|
|
90623
90671
|
assembleProjectDocumentRaw: () => assembleProjectDocumentRaw,
|
|
90624
|
-
createDocumentPageReader: () => createDocumentPageReader,
|
|
90625
90672
|
fetchProjectDocumentPartitionRecords: () => fetchProjectDocumentPartitionRecords,
|
|
90626
90673
|
fetchProjectDocumentRawChunked: () => fetchProjectDocumentRawChunked,
|
|
90627
90674
|
fetchProjectDocumentRawChunkedOnce: () => fetchProjectDocumentRawChunkedOnce,
|
|
@@ -130418,14 +130465,15 @@ function retainedStoredClassBody(context, storedClassId, baseBody) {
|
|
|
130418
130465
|
const storedMemberIds = storedClassSchemaMemberIds(context, storedClassId);
|
|
130419
130466
|
const body = {};
|
|
130420
130467
|
for (const [key, childId] of Object.entries(baseBody)) {
|
|
130421
|
-
|
|
130422
|
-
if (storedMemberId !== void 0 && !context.members.has(storedMemberId)) {
|
|
130423
|
-
continue;
|
|
130424
|
-
}
|
|
130468
|
+
if (storedMemberRemoved(context, storedMemberIds, key)) continue;
|
|
130425
130469
|
body[key] = childId;
|
|
130426
130470
|
}
|
|
130427
130471
|
return body;
|
|
130428
130472
|
}
|
|
130473
|
+
function storedMemberRemoved(context, storedMemberIds, key) {
|
|
130474
|
+
const storedMemberId = storedMemberIds.get(key);
|
|
130475
|
+
return storedMemberId !== void 0 && !context.members.has(storedMemberId);
|
|
130476
|
+
}
|
|
130429
130477
|
function storedClassSchemaMemberIds(context, classId) {
|
|
130430
130478
|
const cached = context.storedClassSchemaMemberIds.get(classId);
|
|
130431
130479
|
if (cached !== void 0) return cached;
|
|
@@ -131745,7 +131793,7 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
|
|
|
131745
131793
|
}
|
|
131746
131794
|
for (const assignment of expression.initializer ?? []) {
|
|
131747
131795
|
const childMember = classMemberByName(context, classId, assignment.name);
|
|
131748
|
-
const childId =
|
|
131796
|
+
const childId = body[assignment.name];
|
|
131749
131797
|
if (typeof childId !== "string") {
|
|
131750
131798
|
const symbolId2 = sourceValueSymbol(context, assignment.value);
|
|
131751
131799
|
if (symbolId2 !== null) {
|
|
@@ -131817,6 +131865,7 @@ function lowerClassValue(context, member, expression, base, source, outerEnviron
|
|
|
131817
131865
|
for (const [schemaKey, childId] of Object.entries(baseBody)) {
|
|
131818
131866
|
if (typeof childId !== "string") continue;
|
|
131819
131867
|
if (resetSchemaKeys.has(schemaKey)) continue;
|
|
131868
|
+
if (storedMemberRemoved(context, storedMemberIds, schemaKey)) continue;
|
|
131820
131869
|
if (materializedConstruction === "lower" && authoredKeys.has(schemaKey)) {
|
|
131821
131870
|
continue;
|
|
131822
131871
|
}
|
|
@@ -179970,14 +180019,28 @@ async function runExport(workspace, options) {
|
|
|
179970
180019
|
workspace,
|
|
179971
180020
|
options.unityProject
|
|
179972
180021
|
);
|
|
179973
|
-
const
|
|
179974
|
-
|
|
179975
|
-
|
|
179976
|
-
|
|
179977
|
-
|
|
179978
|
-
|
|
180022
|
+
const progress = spinner("Reading pulled project\u2026");
|
|
180023
|
+
let result;
|
|
180024
|
+
let local;
|
|
180025
|
+
try {
|
|
180026
|
+
result = exportUnityProject({
|
|
180027
|
+
workspace,
|
|
180028
|
+
exportSettings,
|
|
180029
|
+
convexUrl,
|
|
180030
|
+
target,
|
|
180031
|
+
onPhase: (label) => progress.update(label)
|
|
180032
|
+
});
|
|
180033
|
+
progress.update("Checking local changes\u2026");
|
|
180034
|
+
local = localChanges(workspace);
|
|
180035
|
+
} catch (error) {
|
|
180036
|
+
progress.fail("Export failed.");
|
|
180037
|
+
throw error;
|
|
180038
|
+
}
|
|
179979
180039
|
const { phases } = result;
|
|
179980
180040
|
const elapsed = phases.read + phases.build + phases.codegen + phases.write;
|
|
180041
|
+
progress.succeed(
|
|
180042
|
+
`Exported ${color.bold(exportSettings.semver.label)} ${color.dim(`(${workspace.config.versionId})`)} into ${target.unityProjectRoot} in ${elapsed} ms ${color.dim(`(read ${phases.read}, build ${phases.build}, codegen ${phases.codegen}, write ${phases.write})`)}.`
|
|
180043
|
+
);
|
|
179981
180044
|
for (const diagnostic of result.diagnostics) {
|
|
179982
180045
|
if (diagnostic.severity === "error") continue;
|
|
179983
180046
|
warn(formatDiagnostic(diagnostic));
|
|
@@ -179987,16 +180050,12 @@ async function runExport(workspace, options) {
|
|
|
179987
180050
|
`Unity's project.json was written at schema ${result.schemaSkew}; this export writes schema ${PROJECT_UNITY_EXPORT_SCHEMA_VERSION}. Update the Neo Compose SDK in Unity to a version that reads schema ${PROJECT_UNITY_EXPORT_SCHEMA_VERSION}.`
|
|
179988
180051
|
);
|
|
179989
180052
|
}
|
|
179990
|
-
success(
|
|
179991
|
-
`Exported ${color.bold(exportSettings.semver.label)} ${color.dim(`(${workspace.config.versionId})`)} into ${target.unityProjectRoot} in ${elapsed} ms ${color.dim(`(read ${phases.read}, build ${phases.build}, codegen ${phases.codegen}, write ${phases.write})`)}.`
|
|
179992
|
-
);
|
|
179993
180053
|
console.log(
|
|
179994
180054
|
` ${result.written.length} file(s) written, ${result.deleted.length} deleted, ${result.unchanged} unchanged; codegen ${result.codegenRan ? "ran" : "skipped"}.`
|
|
179995
180055
|
);
|
|
179996
180056
|
console.log(
|
|
179997
180057
|
result.sidecarWritten ? " Unity ingests the export on its next editor tick." : " Unity is current."
|
|
179998
180058
|
);
|
|
179999
|
-
const local = localChanges(workspace);
|
|
180000
180059
|
if (local.errors > 0) {
|
|
180001
180060
|
note(
|
|
180002
180061
|
` Local sources have ${local.errors} error(s) and are not in the export, which holds the pulled head. Run \`neo status\` to see them.`
|
|
@@ -180008,7 +180067,7 @@ async function runExport(workspace, options) {
|
|
|
180008
180067
|
}
|
|
180009
180068
|
}
|
|
180010
180069
|
function exportUnityProject(args) {
|
|
180011
|
-
const { workspace, exportSettings, target } = args;
|
|
180070
|
+
const { workspace, exportSettings, target, onPhase } = args;
|
|
180012
180071
|
const { projectId, versionId } = workspace.config;
|
|
180013
180072
|
let started = performance.now();
|
|
180014
180073
|
const lap = () => {
|
|
@@ -180025,6 +180084,7 @@ function exportUnityProject(args) {
|
|
|
180025
180084
|
})
|
|
180026
180085
|
);
|
|
180027
180086
|
const read = lap();
|
|
180087
|
+
onPhase(`Building export from ${Object.keys(records2).length} records\u2026`);
|
|
180028
180088
|
const unityExport = buildProjectUnityExport(document);
|
|
180029
180089
|
const files = pulledExportFiles(workspace, Object.keys(unityExport.files));
|
|
180030
180090
|
const recordHashes = {};
|
|
@@ -180039,6 +180099,7 @@ function exportUnityProject(args) {
|
|
|
180039
180099
|
});
|
|
180040
180100
|
const projectDiagnostics2 = buildUnityExportProjectDiagnostics(document);
|
|
180041
180101
|
const build = lap();
|
|
180102
|
+
if (!skipCodegen) onPhase("Generating Unity types\u2026");
|
|
180042
180103
|
const generated = skipCodegen ? null : generateUnityTypes(unityExport, buildUnityCodegenDocs(document));
|
|
180043
180104
|
const codegen = lap();
|
|
180044
180105
|
const diagnostics = [
|
|
@@ -180090,6 +180151,7 @@ ${errors.map((error) => ` ${formatDiagnostic(error)}`).join("\n")}`
|
|
|
180090
180151
|
projectJsonPath,
|
|
180091
180152
|
dataFiles.get(projectJsonPath)
|
|
180092
180153
|
);
|
|
180154
|
+
onPhase(`Writing changed files into ${target.unityProjectRoot}\u2026`);
|
|
180093
180155
|
const written = [];
|
|
180094
180156
|
let unchanged = 0;
|
|
180095
180157
|
for (const [path, content] of [...dataFiles, ...localizationFiles.writes]) {
|
|
@@ -180938,7 +181000,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
|
|
|
180938
181000
|
async function main() {
|
|
180939
181001
|
const args = parseArgs(process.argv.slice(2));
|
|
180940
181002
|
if (args.command === "--version") {
|
|
180941
|
-
console.log("0.69.
|
|
181003
|
+
console.log("0.69.1");
|
|
180942
181004
|
return;
|
|
180943
181005
|
}
|
|
180944
181006
|
if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
|
package/dist/test-worker.mjs
CHANGED
|
@@ -37258,6 +37258,17 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
|
|
|
37258
37258
|
targetType,
|
|
37259
37259
|
schemaClass
|
|
37260
37260
|
);
|
|
37261
|
+
if (memberType.kind === "action") {
|
|
37262
|
+
return {
|
|
37263
|
+
schemaKey: member.schemaKey ?? member.name,
|
|
37264
|
+
memberId: member.id,
|
|
37265
|
+
valuePointer: this.resolveReplayedListenerSet(
|
|
37266
|
+
entry.value,
|
|
37267
|
+
memberType,
|
|
37268
|
+
scope
|
|
37269
|
+
)
|
|
37270
|
+
};
|
|
37271
|
+
}
|
|
37261
37272
|
const value = this.resolveExpression(entry.value, scope, memberType);
|
|
37262
37273
|
if (!isNeoScriptTypeAssignable(value.type, memberType, this.project)) {
|
|
37263
37274
|
throw new CompileError(
|
|
@@ -37272,6 +37283,43 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
|
|
|
37272
37283
|
};
|
|
37273
37284
|
});
|
|
37274
37285
|
}
|
|
37286
|
+
/**
|
|
37287
|
+
* P62 §2. A stored action slot replays as the listener list it stores. Each
|
|
37288
|
+
* entry resolves to the same static (member, row) identity the authored list
|
|
37289
|
+
* lowers to. Executable script cannot reassign a listener set (§3.2).
|
|
37290
|
+
*/
|
|
37291
|
+
resolveReplayedListenerSet(expression, actionType, scope) {
|
|
37292
|
+
if (this.context.storedConstructionReplay !== true) {
|
|
37293
|
+
throw new CompileError(
|
|
37294
|
+
`Cannot assign to ${this.describe(actionType)}; a listener set is not reassignable from script. Use '+=' to add a listener or '-=' to remove one.`,
|
|
37295
|
+
expression.pos
|
|
37296
|
+
);
|
|
37297
|
+
}
|
|
37298
|
+
if (expression.kind !== "litList") {
|
|
37299
|
+
throw new CompileError(
|
|
37300
|
+
`A stored ${this.describe(actionType)} replays as a bracketed listener list.`,
|
|
37301
|
+
expression.pos
|
|
37302
|
+
);
|
|
37303
|
+
}
|
|
37304
|
+
const listeners = [];
|
|
37305
|
+
for (const element of expression.elements) {
|
|
37306
|
+
const listener = this.resolveActionListener(element, actionType, scope);
|
|
37307
|
+
if (listener.type !== "value" /* Value */) {
|
|
37308
|
+
throw new CompileError(
|
|
37309
|
+
"A stored listener must name a stable (member, row) target.",
|
|
37310
|
+
element.pos
|
|
37311
|
+
);
|
|
37312
|
+
}
|
|
37313
|
+
listeners.push(listener.value.value);
|
|
37314
|
+
}
|
|
37315
|
+
return {
|
|
37316
|
+
type: "value" /* Value */,
|
|
37317
|
+
value: {
|
|
37318
|
+
typeInfo: toWireType(actionType, this.project),
|
|
37319
|
+
value: { listeners }
|
|
37320
|
+
}
|
|
37321
|
+
};
|
|
37322
|
+
}
|
|
37275
37323
|
resolveTileConversion(callee, args, typeArguments, scope, pos) {
|
|
37276
37324
|
const receiver = this.resolveExpression(callee.receiver, scope);
|
|
37277
37325
|
const owner = receiver.type.kind === "named" ? this.project.typeById.get(receiver.type.typeId) : void 0;
|
|
@@ -43398,7 +43446,6 @@ var project_document_read_exports = {};
|
|
|
43398
43446
|
__export(project_document_read_exports, {
|
|
43399
43447
|
DOCUMENT_MANIFEST_PAGE_SIZE: () => DOCUMENT_MANIFEST_PAGE_SIZE,
|
|
43400
43448
|
assembleProjectDocumentRaw: () => assembleProjectDocumentRaw,
|
|
43401
|
-
createDocumentPageReader: () => createDocumentPageReader,
|
|
43402
43449
|
fetchProjectDocumentPartitionRecords: () => fetchProjectDocumentPartitionRecords,
|
|
43403
43450
|
fetchProjectDocumentRawChunked: () => fetchProjectDocumentRawChunked,
|
|
43404
43451
|
fetchProjectDocumentRawChunkedOnce: () => fetchProjectDocumentRawChunkedOnce,
|
package/package.json
CHANGED
|
@@ -110,7 +110,7 @@ wrappers.
|
|
|
110
110
|
The marker near the top of `SKILL.md` must exactly match the package version:
|
|
111
111
|
|
|
112
112
|
```html
|
|
113
|
-
<!-- reviewed-through-cli: 0.69.
|
|
113
|
+
<!-- reviewed-through-cli: 0.69.1 -->
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
The quoted version above is checked too, so this instruction cannot go stale
|