@neocompose/cli 0.70.0 → 0.70.2
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,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.70.2] - 2026-10-06
|
|
4
|
+
|
|
5
|
+
- After `neo push` creates, replaces or moves a project file, its committed bytes become the file's merge base at its new path, as a pull's would. Before, the re-export after the push failed for every new file with "the pull recorded no bytes for it", and for a moved file with "missing". Either failure left the Unity project marked as a local export.
|
|
6
|
+
|
|
7
|
+
## [0.70.1] - 2026-10-06
|
|
8
|
+
|
|
9
|
+
- `neo push` can upload a project file renamed with `@settings(name: ...)`. It used to stage the upload under the file's basename, so the commit failed with "Staged project file … did not match its source name".
|
|
10
|
+
- A project file's `@settings(name: ...)` must end with its file's extension, for example `@settings(name: "ClinkingCoin1.mp3")` for `clinkingcoin1.mp3`. A pull on another machine rebuilds the file's path from that name. The symbol error now says the name needs that extension.
|
|
11
|
+
- Push and pull keep `@settings(name: ...)` on a renamed file, and a new file keeps the path you gave it. Before, the rewrite after a push dropped the name and rebuilt the path from it.
|
|
12
|
+
|
|
3
13
|
## [0.70.0] - 2026-10-06
|
|
4
14
|
|
|
5
15
|
- `neo export` exports the working copy, local edits included, with no network. Unity sees an edit as soon as it's saved, before `neo push`. The export says whether it holds only the pulled head ("Clean") or how many local changes it includes ("Local"). A local export is marked in `metadata.localExport`; SDK 0.59.0 refuses player builds from it and keeps play-mode saves on disk instead of the server. A clean `neo export` or a push clears the mark. A local export still stops, writing nothing, on an error diagnostic or on a project file whose pulled bytes aren't on disk yet; run `neo pull` first.
|
package/dist/neo.mjs
CHANGED
|
@@ -31811,7 +31811,7 @@ var init_registry = __esm({
|
|
|
31811
31811
|
"../packages/manifest/schema-contract/registry.mjs"() {
|
|
31812
31812
|
"use strict";
|
|
31813
31813
|
PROJECT_SCHEMA_CONTRACT = Object.freeze({
|
|
31814
|
-
cliVersion: "0.70.
|
|
31814
|
+
cliVersion: "0.70.2",
|
|
31815
31815
|
projectFileUploadBatchSize: 32,
|
|
31816
31816
|
documentRecords: {
|
|
31817
31817
|
member: {
|
|
@@ -60588,7 +60588,7 @@ function validateProjectFileDeclarationSymbolsV4(declarations) {
|
|
|
60588
60588
|
const expected = expectedSymbols.get(declaration.recordId);
|
|
60589
60589
|
if (expected === void 0 || declaration.symbol === expected) continue;
|
|
60590
60590
|
throw new Error(
|
|
60591
|
-
`Project file ${declaration.symbol} must use registry symbol ${expected}, derived from its persisted name ${JSON.stringify(declaration.persistedName)}. Rename the declaration or set @settings(name: ...)
|
|
60591
|
+
`Project file ${declaration.symbol} must use registry symbol ${expected}, derived from its persisted name ${JSON.stringify(declaration.persistedName)}. Rename the declaration, or set @settings(name: ...) to a file name ending in ${JSON.stringify(sourceExtension(declaration.persistedName))} that derives ${declaration.symbol}.`
|
|
60592
60592
|
);
|
|
60593
60593
|
}
|
|
60594
60594
|
}
|
|
@@ -60621,7 +60621,6 @@ function emitProjectFileRegistrySourcesV4(records2, binaryPaths = /* @__PURE__ *
|
|
|
60621
60621
|
const registryType = kind === "image" ? "ImageRegistry" : "AudioClipRegistry";
|
|
60622
60622
|
const registryName = kind === "image" ? "Images" : "AudioClips";
|
|
60623
60623
|
const valueType = kind === "image" ? "NeoImage" : "NeoAudioClip";
|
|
60624
|
-
const directory = kind === "image" ? "Files/Images" : "Files/AudioClips";
|
|
60625
60624
|
const entries = liveFiles.filter(({ data }) => data.fileType === kind).sort((left, right) => {
|
|
60626
60625
|
const leftSymbol = symbols.get(left.record.recordId) ?? "";
|
|
60627
60626
|
const rightSymbol = symbols.get(right.record.recordId) ?? "";
|
|
@@ -60638,12 +60637,16 @@ function emitProjectFileRegistrySourcesV4(records2, binaryPaths = /* @__PURE__ *
|
|
|
60638
60637
|
const templateName = templateId === null ? null : (kind === "image" ? textureTemplates : audioTemplates).get(
|
|
60639
60638
|
templateId
|
|
60640
60639
|
);
|
|
60641
|
-
const annotation2 = templateName ? ` @settings(template: ${templateName})
|
|
60642
|
-
` : "";
|
|
60643
60640
|
const path = normalizeTrustedProjectBinaryPath(
|
|
60644
|
-
binaryPaths.get(record6.recordId) ??
|
|
60641
|
+
binaryPaths.get(record6.recordId) ?? canonicalProjectBinaryPathV4(data),
|
|
60645
60642
|
kind
|
|
60646
60643
|
);
|
|
60644
|
+
const options = [
|
|
60645
|
+
...templateName ? [`template: ${templateName}`] : [],
|
|
60646
|
+
...data.name === sourceBasename(path) ? [] : [`name: ${JSON.stringify(data.name)}`]
|
|
60647
|
+
];
|
|
60648
|
+
const annotation2 = options.length === 0 ? "" : ` @settings(${options.join(", ")})
|
|
60649
|
+
`;
|
|
60647
60650
|
return ` @id(${JSON.stringify(record6.recordId)})
|
|
60648
60651
|
${annotation2} ${valueType} ${symbol} = new(${JSON.stringify(path)});`;
|
|
60649
60652
|
});
|
|
@@ -60665,10 +60668,7 @@ function projectFileDeclarationsV4(analysis) {
|
|
|
60665
60668
|
);
|
|
60666
60669
|
}
|
|
60667
60670
|
function lowerTrustedProjectFileRegistrySourcesV4(state, analysis, trustedPendingFiles = /* @__PURE__ */ new Map()) {
|
|
60668
|
-
const
|
|
60669
|
-
const declarations = analysis.files.registries.flatMap(
|
|
60670
|
-
(registry) => parseRegistry(registry, templateIds)
|
|
60671
|
-
);
|
|
60671
|
+
const declarations = projectFileDeclarationsV4(analysis);
|
|
60672
60672
|
validateProjectFileDeclarationSymbolsV4(declarations);
|
|
60673
60673
|
const seenPaths = /* @__PURE__ */ new Map();
|
|
60674
60674
|
const seenSymbols = /* @__PURE__ */ new Map();
|
|
@@ -60848,7 +60848,7 @@ function canonicalProjectBinaryPathV4(data) {
|
|
|
60848
60848
|
if (typeof data.name !== "string") {
|
|
60849
60849
|
throw new Error("Project file name is missing.");
|
|
60850
60850
|
}
|
|
60851
|
-
return `${kind === "image" ? "Files/Images" : "Files/AudioClips"}/${
|
|
60851
|
+
return `${kind === "image" ? "Files/Images" : "Files/AudioClips"}/${safeProjectFileName(data.name)}`;
|
|
60852
60852
|
}
|
|
60853
60853
|
function fileTemplateIdsByName(analysis) {
|
|
60854
60854
|
const templateIds = /* @__PURE__ */ new Map();
|
|
@@ -60900,13 +60900,22 @@ function parseRegistry(registry, templateIds) {
|
|
|
60900
60900
|
const settings = parseFileSettings(match[2], templateIds);
|
|
60901
60901
|
const path = JSON.parse(match[5]);
|
|
60902
60902
|
const recordId = match[1] ? JSON.parse(match[1]) : `__pending__:project-file:${encodeURIComponent(registry.source.uri)}:${registry.source.range.start.line}:${encodeURIComponent(match[4])}`;
|
|
60903
|
+
const fileName2 = sourceBasename(path);
|
|
60904
|
+
if (settings.name !== null) {
|
|
60905
|
+
const extension = sourceExtension(fileName2);
|
|
60906
|
+
if (!settings.name.toLowerCase().endsWith(extension.toLowerCase())) {
|
|
60907
|
+
throw new Error(
|
|
60908
|
+
`Project file ${match[4]} @settings(name: ${JSON.stringify(settings.name)}) must end with ${JSON.stringify(extension)}, the extension of ${JSON.stringify(path)}.`
|
|
60909
|
+
);
|
|
60910
|
+
}
|
|
60911
|
+
}
|
|
60903
60912
|
result.push({
|
|
60904
60913
|
recordId,
|
|
60905
60914
|
symbol: match[4],
|
|
60906
60915
|
path,
|
|
60907
60916
|
kind,
|
|
60908
60917
|
templateId: settings.templateId,
|
|
60909
|
-
persistedName: settings.name ??
|
|
60918
|
+
persistedName: settings.name ?? fileName2,
|
|
60910
60919
|
sourceFile: registry.source.uri,
|
|
60911
60920
|
sourceLine: registry.source.range.start.line + 1
|
|
60912
60921
|
});
|
|
@@ -60992,7 +61001,7 @@ function templateNames(records2, kind) {
|
|
|
60992
61001
|
})
|
|
60993
61002
|
);
|
|
60994
61003
|
}
|
|
60995
|
-
function
|
|
61004
|
+
function safeProjectFileName(value) {
|
|
60996
61005
|
const fileName2 = sourceBasename(value).replace(/[\\/]/g, "-");
|
|
60997
61006
|
if (fileName2 === "." || fileName2 === ".." || fileName2.length === 0) {
|
|
60998
61007
|
throw new Error(`Invalid project filename ${JSON.stringify(value)}.`);
|
|
@@ -61002,6 +61011,9 @@ function safeFileName(value) {
|
|
|
61002
61011
|
function sourceBasename(value) {
|
|
61003
61012
|
return value.replaceAll("\\", "/").split("/").at(-1) ?? "";
|
|
61004
61013
|
}
|
|
61014
|
+
function sourceExtension(value) {
|
|
61015
|
+
return /\.[^.]+$/.exec(value)?.[0] ?? "";
|
|
61016
|
+
}
|
|
61005
61017
|
function optionalString(value) {
|
|
61006
61018
|
return typeof value === "string" ? value : null;
|
|
61007
61019
|
}
|
|
@@ -136733,10 +136745,7 @@ import {
|
|
|
136733
136745
|
} from "node:fs";
|
|
136734
136746
|
import { basename, dirname as dirname7, extname, join as join6, relative as relative2, sep as sep2 } from "node:path";
|
|
136735
136747
|
function lowerProjectFileRegistrySourcesV4(root, state, analysis, options = {}) {
|
|
136736
|
-
const
|
|
136737
|
-
const declarations = analysis.files.registries.flatMap(
|
|
136738
|
-
(registry) => parseRegistry2(registry, templateIds)
|
|
136739
|
-
);
|
|
136748
|
+
const declarations = projectFileDeclarationsV4(analysis);
|
|
136740
136749
|
validateProjectFileDeclarationSymbolsV4(declarations);
|
|
136741
136750
|
const seenPaths = /* @__PURE__ */ new Map();
|
|
136742
136751
|
const seenSymbols = /* @__PURE__ */ new Map();
|
|
@@ -136821,14 +136830,8 @@ function lowerProjectFileRegistrySourcesV4(root, state, analysis, options = {})
|
|
|
136821
136830
|
};
|
|
136822
136831
|
});
|
|
136823
136832
|
}
|
|
136824
|
-
function projectFileDeclarationsV42(analysis) {
|
|
136825
|
-
const templateIds = fileTemplateIdsByName2(analysis);
|
|
136826
|
-
return analysis.files.registries.flatMap(
|
|
136827
|
-
(registry) => parseRegistry2(registry, templateIds)
|
|
136828
|
-
);
|
|
136829
|
-
}
|
|
136830
136833
|
function lowerDiscoveredProjectBinariesV4(root, state, analysis) {
|
|
136831
|
-
const explicit =
|
|
136834
|
+
const explicit = projectFileDeclarationsV4(analysis);
|
|
136832
136835
|
const project = Object.values(state).find(
|
|
136833
136836
|
(entry) => entry.recordKind === "project" && isObjectRecord2(entry.data)
|
|
136834
136837
|
);
|
|
@@ -136869,7 +136872,7 @@ function lowerDiscoveredProjectBinariesV4(root, state, analysis) {
|
|
|
136869
136872
|
});
|
|
136870
136873
|
}
|
|
136871
136874
|
function inspectProjectBinaryStatusV4(root, state, analysis) {
|
|
136872
|
-
const explicit =
|
|
136875
|
+
const explicit = projectFileDeclarationsV4(analysis);
|
|
136873
136876
|
const result = explicit.map((declaration) => {
|
|
136874
136877
|
const baseState = state[`project-file:${declaration.recordId}`];
|
|
136875
136878
|
const data = isObjectRecord2(baseState?.data) ? baseState.data : {};
|
|
@@ -136927,16 +136930,6 @@ function inspectProjectBinaryStatusV4(root, state, analysis) {
|
|
|
136927
136930
|
function pendingDiscoveredFileId(path) {
|
|
136928
136931
|
return `__pending__:project-file:drag:${encodeURIComponent(normalizeSlash2(path))}`;
|
|
136929
136932
|
}
|
|
136930
|
-
function canonicalProjectBinaryPathV42(data) {
|
|
136931
|
-
if (!isObjectRecord2(data))
|
|
136932
|
-
throw new Error("Project file metadata must be an object.");
|
|
136933
|
-
if (data.fileType !== "image" && data.fileType !== "audio")
|
|
136934
|
-
throw new Error("Project file metadata has an unsupported fileType.");
|
|
136935
|
-
if (typeof data.name !== "string")
|
|
136936
|
-
throw new Error("Project file metadata has no filename.");
|
|
136937
|
-
const directory = data.fileType === "image" ? "Files/Images" : "Files/AudioClips";
|
|
136938
|
-
return `${directory}/${safeFileName2(data.name)}`;
|
|
136939
|
-
}
|
|
136940
136933
|
function discoverProjectBinariesV4(root, explicit, ignoredPaths = []) {
|
|
136941
136934
|
const explicitPaths = new Set(
|
|
136942
136935
|
[...explicit.map((entry) => entry.path), ...ignoredPaths].map(
|
|
@@ -137012,124 +137005,11 @@ function writeBinaryConflictArtifactV4(root, fileId, fileName2, bytes, expectedS
|
|
|
137012
137005
|
"conflicts",
|
|
137013
137006
|
"files",
|
|
137014
137007
|
safePathSegment2(fileId),
|
|
137015
|
-
|
|
137008
|
+
safeProjectFileName(fileName2)
|
|
137016
137009
|
);
|
|
137017
137010
|
writeVerifiedBinaryDownloadV4(destination, bytes, expectedSha256);
|
|
137018
137011
|
return normalizeSlash2(relative2(root, destination));
|
|
137019
137012
|
}
|
|
137020
|
-
function fileTemplateIdsByName2(analysis) {
|
|
137021
|
-
const templateIds = /* @__PURE__ */ new Map();
|
|
137022
|
-
for (const global of analysis.configuration.globals) {
|
|
137023
|
-
const kind = global.type.name === "TextureTemplate" ? "texture-template" : global.type.name === "AudioTemplate" ? "audio-template" : null;
|
|
137024
|
-
if (kind === null) continue;
|
|
137025
|
-
templateIds.set(
|
|
137026
|
-
global.name,
|
|
137027
|
-
global.id ?? `__pending__:${kind}:${encodeURIComponent(global.source.uri)}:${global.source.range.start.line}:${global.source.range.start.character}:${encodeURIComponent(global.name)}`
|
|
137028
|
-
);
|
|
137029
|
-
}
|
|
137030
|
-
return templateIds;
|
|
137031
|
-
}
|
|
137032
|
-
function parseRegistry2(registry, templateIds) {
|
|
137033
|
-
const kind = registry.type.name === "ImageRegistry" ? "image" : registry.type.name === "AudioClipRegistry" ? "audio" : null;
|
|
137034
|
-
if (kind === null)
|
|
137035
|
-
throw new Error(
|
|
137036
|
-
`Unsupported project file registry type ${registry.type.name}.`
|
|
137037
|
-
);
|
|
137038
|
-
const expectedName = kind === "image" ? "Images" : "AudioClips";
|
|
137039
|
-
if (registry.name !== expectedName)
|
|
137040
|
-
throw new Error(
|
|
137041
|
-
`${registry.type.name} must use the canonical global name ${expectedName}.`
|
|
137042
|
-
);
|
|
137043
|
-
const body = objectInitializerBody2(registry.initializer, registry.type.name);
|
|
137044
|
-
const result = [];
|
|
137045
|
-
let cursor = 0;
|
|
137046
|
-
while (cursor < body.length) {
|
|
137047
|
-
const whitespace = /^(?:\s|\/\/[^\n]*(?:\n|$)|\/\*[\s\S]*?\*\/)+/.exec(
|
|
137048
|
-
body.slice(cursor)
|
|
137049
|
-
);
|
|
137050
|
-
if (whitespace) cursor += whitespace[0].length;
|
|
137051
|
-
if (cursor >= body.length) break;
|
|
137052
|
-
REGISTRY_ENTRY2.lastIndex = cursor;
|
|
137053
|
-
const match = REGISTRY_ENTRY2.exec(body);
|
|
137054
|
-
if (!match)
|
|
137055
|
-
throw new Error(
|
|
137056
|
-
`${registry.type.name} ${registry.name} contains an invalid file declaration near ${JSON.stringify(body.slice(cursor, cursor + 80))}.`
|
|
137057
|
-
);
|
|
137058
|
-
const entryType = match[3];
|
|
137059
|
-
if (kind === "image" && entryType !== "NeoImage" || kind === "audio" && entryType !== "NeoAudioClip") {
|
|
137060
|
-
throw new Error(
|
|
137061
|
-
`${registry.type.name} cannot contain ${entryType} declarations.`
|
|
137062
|
-
);
|
|
137063
|
-
}
|
|
137064
|
-
const settings = parseFileSettings2(match[2], templateIds);
|
|
137065
|
-
const path = JSON.parse(match[5]);
|
|
137066
|
-
const recordId = match[1] ? JSON.parse(match[1]) : `__pending__:project-file:${encodeURIComponent(registry.source.uri)}:${registry.source.range.start.line}:${encodeURIComponent(match[4])}`;
|
|
137067
|
-
result.push({
|
|
137068
|
-
recordId,
|
|
137069
|
-
symbol: match[4],
|
|
137070
|
-
path,
|
|
137071
|
-
kind,
|
|
137072
|
-
templateId: settings.templateId,
|
|
137073
|
-
persistedName: settings.name ?? basename(path),
|
|
137074
|
-
sourceFile: registry.source.uri,
|
|
137075
|
-
sourceLine: registry.source.range.start.line + 1
|
|
137076
|
-
});
|
|
137077
|
-
cursor = REGISTRY_ENTRY2.lastIndex;
|
|
137078
|
-
}
|
|
137079
|
-
return result;
|
|
137080
|
-
}
|
|
137081
|
-
function parseFileSettings2(source, templateIds) {
|
|
137082
|
-
if (!source) return { templateId: null, name: null };
|
|
137083
|
-
const expression = parseExpression(`new(${source})`);
|
|
137084
|
-
if (expression.kind !== "new")
|
|
137085
|
-
throw new Error("File @settings arguments are invalid.");
|
|
137086
|
-
let templateId = null;
|
|
137087
|
-
let name = null;
|
|
137088
|
-
expression.args.forEach((argument2, index) => {
|
|
137089
|
-
const argumentName = expression.argumentNames?.[index];
|
|
137090
|
-
if (!argumentName)
|
|
137091
|
-
throw new Error("File @settings arguments must be named.");
|
|
137092
|
-
if (argumentName === "template") {
|
|
137093
|
-
templateId = resolveTemplate2(argument2, templateIds);
|
|
137094
|
-
return;
|
|
137095
|
-
}
|
|
137096
|
-
if (argumentName === "name") {
|
|
137097
|
-
if (argument2.kind !== "litString")
|
|
137098
|
-
throw new Error("File @settings name must be a string literal.");
|
|
137099
|
-
name = argument2.value;
|
|
137100
|
-
return;
|
|
137101
|
-
}
|
|
137102
|
-
throw new Error(
|
|
137103
|
-
`Unknown project file @settings option ${argumentName}; expected template or name.`
|
|
137104
|
-
);
|
|
137105
|
-
});
|
|
137106
|
-
return { templateId, name };
|
|
137107
|
-
}
|
|
137108
|
-
function resolveTemplate2(expression, templates) {
|
|
137109
|
-
if (expression.kind === "litNull") return null;
|
|
137110
|
-
if (expression.kind === "ident") {
|
|
137111
|
-
const id2 = templates.get(expression.name);
|
|
137112
|
-
if (!id2)
|
|
137113
|
-
throw new Error(`Unknown file import template ${expression.name}.`);
|
|
137114
|
-
return id2;
|
|
137115
|
-
}
|
|
137116
|
-
if (expression.kind === "call" && expression.callee.kind === "ident" && expression.callee.name === "Reference") {
|
|
137117
|
-
const index = expression.argumentNames?.findIndex((name) => name === "id") ?? -1;
|
|
137118
|
-
const id2 = expression.args[index >= 0 ? index : 0];
|
|
137119
|
-
if (id2?.kind === "litString") return id2.value;
|
|
137120
|
-
}
|
|
137121
|
-
throw new Error(
|
|
137122
|
-
"File template must be a template symbol, Reference(id: ...), or null."
|
|
137123
|
-
);
|
|
137124
|
-
}
|
|
137125
|
-
function objectInitializerBody2(initializer, typeName) {
|
|
137126
|
-
const open = initializer.indexOf("{");
|
|
137127
|
-
const close = initializer.lastIndexOf("}");
|
|
137128
|
-
if (!/^new\s*\(\s*\)/.test(initializer.trim()) || open < 0 || close < open) {
|
|
137129
|
-
throw new Error(`${typeName} must use new() { ... }.`);
|
|
137130
|
-
}
|
|
137131
|
-
return initializer.slice(open + 1, close);
|
|
137132
|
-
}
|
|
137133
137013
|
function optionalString3(value) {
|
|
137134
137014
|
return typeof value === "string" ? value : null;
|
|
137135
137015
|
}
|
|
@@ -137178,25 +137058,18 @@ function visitBinaryFiles(directory, visit) {
|
|
|
137178
137058
|
visit(path);
|
|
137179
137059
|
}
|
|
137180
137060
|
}
|
|
137181
|
-
function safeFileName2(value) {
|
|
137182
|
-
const fileName2 = basename(value).replace(/[\\/]/g, "-");
|
|
137183
|
-
if (fileName2 === "." || fileName2 === ".." || fileName2.length === 0)
|
|
137184
|
-
throw new Error(`Invalid project filename ${JSON.stringify(value)}.`);
|
|
137185
|
-
return fileName2;
|
|
137186
|
-
}
|
|
137187
137061
|
function safePathSegment2(value) {
|
|
137188
137062
|
return value.replace(/[^A-Za-z0-9._-]/g, "_") || "file";
|
|
137189
137063
|
}
|
|
137190
137064
|
function normalizeSlash2(value) {
|
|
137191
137065
|
return value.split(sep2).join("/").replaceAll("\\", "/");
|
|
137192
137066
|
}
|
|
137193
|
-
var SUPPORTED_BINARY_TYPES
|
|
137067
|
+
var SUPPORTED_BINARY_TYPES;
|
|
137194
137068
|
var init_project_files = __esm({
|
|
137195
137069
|
"src/project-source/project-files.ts"() {
|
|
137196
137070
|
"use strict";
|
|
137197
137071
|
init_project_file_source();
|
|
137198
137072
|
init_workspace_source_path();
|
|
137199
|
-
init_executable_parser();
|
|
137200
137073
|
init_value_comparison();
|
|
137201
137074
|
init_source_format();
|
|
137202
137075
|
init_project_file_source();
|
|
@@ -137210,7 +137083,6 @@ var init_project_files = __esm({
|
|
|
137210
137083
|
[".mp3", { kind: "audio", mimeType: "audio/mpeg" }],
|
|
137211
137084
|
[".ogg", { kind: "audio", mimeType: "audio/ogg" }]
|
|
137212
137085
|
]);
|
|
137213
|
-
REGISTRY_ENTRY2 = /(?:@id\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*)?(?:@settings\(([\s\S]*?)\)\s*)?(NeoImage|NeoAudioClip)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*new\(\s*("(?:[^"\\]|\\.)*")\s*\)\s*;/gy;
|
|
137214
137086
|
}
|
|
137215
137087
|
});
|
|
137216
137088
|
|
|
@@ -140008,7 +139880,7 @@ async function pullProjectBinariesV4(args) {
|
|
|
140008
139880
|
const previous = args.previousRecords[key];
|
|
140009
139881
|
const localStatus = localById.get(record6.recordId);
|
|
140010
139882
|
const declarationPresent = args.destructive === true || args.localBinaries === void 0 || previous === void 0 || localStatus !== void 0;
|
|
140011
|
-
const path = localStatus?.path ?? previous?.projectBinary?.path ??
|
|
139883
|
+
const path = localStatus?.path ?? previous?.projectBinary?.path ?? canonicalProjectBinaryPathV4(record6.data);
|
|
140012
139884
|
const absolute = resolvePath(path);
|
|
140013
139885
|
const localDigest = existsSync9(absolute) ? sha256File(absolute) : null;
|
|
140014
139886
|
const baseDigest = previous?.projectBinary?.sha256 ?? readSha256(previous?.data) ?? null;
|
|
@@ -140130,7 +140002,7 @@ async function pullProjectBinariesV4(args) {
|
|
|
140130
140002
|
if (previous.recordKind !== "project-file") continue;
|
|
140131
140003
|
if (args.document.records.has(key)) continue;
|
|
140132
140004
|
const localStatus = localById.get(previous.recordId);
|
|
140133
|
-
const path = localStatus?.path ?? previous.projectBinary?.path ??
|
|
140005
|
+
const path = localStatus?.path ?? previous.projectBinary?.path ?? canonicalProjectBinaryPathV4(previous.data);
|
|
140134
140006
|
const absolute = resolvePath(path);
|
|
140135
140007
|
const localDigest = existsSync9(absolute) ? sha256File(absolute) : null;
|
|
140136
140008
|
const baseDigest = previous.projectBinary?.sha256 ?? readSha256(previous.data) ?? null;
|
|
@@ -140198,8 +140070,8 @@ async function download(data, fileId, client, workspace) {
|
|
|
140198
140070
|
}
|
|
140199
140071
|
return { bytes: response.bytes, sha256: computed };
|
|
140200
140072
|
}
|
|
140201
|
-
function committedProjectBinaryStateV4(
|
|
140202
|
-
return
|
|
140073
|
+
function committedProjectBinaryStateV4(path, data) {
|
|
140074
|
+
return binaryState(path, readSha256(data), isObjectRecord2(data) ? data : {});
|
|
140203
140075
|
}
|
|
140204
140076
|
function binaryState(path, sha2562, data) {
|
|
140205
140077
|
return {
|
|
@@ -140241,13 +140113,14 @@ var init_project_file_pull = __esm({
|
|
|
140241
140113
|
init_concurrency();
|
|
140242
140114
|
init_http();
|
|
140243
140115
|
init_value_comparison();
|
|
140116
|
+
init_project_file_source();
|
|
140244
140117
|
init_project_files();
|
|
140245
140118
|
DOWNLOAD_CONCURRENCY = 8;
|
|
140246
140119
|
}
|
|
140247
140120
|
});
|
|
140248
140121
|
|
|
140249
140122
|
// src/project-source/project-file-push.ts
|
|
140250
|
-
import {
|
|
140123
|
+
import { join as join11 } from "node:path";
|
|
140251
140124
|
import { readFileSync as readFileSync13 } from "node:fs";
|
|
140252
140125
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
140253
140126
|
async function cleanupRejectedProjectFilePushesV4(args) {
|
|
@@ -140323,11 +140196,17 @@ function prepareProjectFilePushesV4(args) {
|
|
|
140323
140196
|
if (mimeType === null) {
|
|
140324
140197
|
throw new Error(`Project file ${binary.path} has no MIME type.`);
|
|
140325
140198
|
}
|
|
140199
|
+
const name = change.nextData.name;
|
|
140200
|
+
if (typeof name !== "string") {
|
|
140201
|
+
throw new Error(
|
|
140202
|
+
`Project file ${recordId} has upload bytes but its source change has no name.`
|
|
140203
|
+
);
|
|
140204
|
+
}
|
|
140326
140205
|
prepared.push({
|
|
140327
140206
|
uploadToken: `${recordId}-${prepared.length}`,
|
|
140328
140207
|
recordId,
|
|
140329
140208
|
replaceFileId: binary.action === "upload" ? recordId : null,
|
|
140330
|
-
name
|
|
140209
|
+
name,
|
|
140331
140210
|
fileType: binary.kind,
|
|
140332
140211
|
mimeType,
|
|
140333
140212
|
byteLength: bytes.byteLength,
|
|
@@ -155834,12 +155713,12 @@ import {
|
|
|
155834
155713
|
unlinkSync as unlinkSync2,
|
|
155835
155714
|
writeFileSync as writeFileSync10
|
|
155836
155715
|
} from "node:fs";
|
|
155837
|
-
import { basename as
|
|
155716
|
+
import { basename as basename2, dirname as dirname8, join as join12 } from "node:path";
|
|
155838
155717
|
function writeUnityFile(path, content) {
|
|
155839
155718
|
mkdirSync9(dirname8(path), { recursive: true });
|
|
155840
155719
|
const temporary = join12(
|
|
155841
155720
|
dirname8(path),
|
|
155842
|
-
`.${
|
|
155721
|
+
`.${basename2(path)}.${randomUUID5()}.tmp`
|
|
155843
155722
|
);
|
|
155844
155723
|
try {
|
|
155845
155724
|
writeFileSync10(temporary, content);
|
|
@@ -158903,6 +158782,18 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
|
|
|
158903
158782
|
data: local.fullData
|
|
158904
158783
|
});
|
|
158905
158784
|
}
|
|
158785
|
+
for (const binary of localStatus.binaryFiles ?? []) {
|
|
158786
|
+
const key = recordStateKey(
|
|
158787
|
+
"project-file",
|
|
158788
|
+
rewriteAssignedString(binary.fileId, replacements)
|
|
158789
|
+
);
|
|
158790
|
+
const record6 = nextRecords[key];
|
|
158791
|
+
if (record6 === void 0 || !changedKeys.has(key)) continue;
|
|
158792
|
+
nextRecords[key] = {
|
|
158793
|
+
...record6,
|
|
158794
|
+
projectBinary: committedProjectBinaryStateV4(binary.path, record6.data)
|
|
158795
|
+
};
|
|
158796
|
+
}
|
|
158906
158797
|
const previousState = workspace.state;
|
|
158907
158798
|
workspace.state = {
|
|
158908
158799
|
...previousState,
|
|
@@ -158921,7 +158812,11 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
|
|
|
158921
158812
|
(id2) => rewritePlacementValueId(id2, replacements)
|
|
158922
158813
|
)
|
|
158923
158814
|
})),
|
|
158924
|
-
localStatus.sourceSnapshot
|
|
158815
|
+
localStatus.sourceSnapshot,
|
|
158816
|
+
localStatus.binaryFiles?.map((binary) => ({
|
|
158817
|
+
fileId: rewriteAssignedString(binary.fileId, replacements),
|
|
158818
|
+
path: binary.path
|
|
158819
|
+
}))
|
|
158925
158820
|
);
|
|
158926
158821
|
} catch (error) {
|
|
158927
158822
|
workspace.state = {
|
|
@@ -159131,14 +159026,13 @@ function foldChangedRecords(previous, changedRecords) {
|
|
|
159131
159026
|
`Changed record "${key}" response is missing its snapshot content hash.`
|
|
159132
159027
|
);
|
|
159133
159028
|
}
|
|
159134
|
-
const projectBinary = records2[key]?.projectBinary;
|
|
159135
159029
|
records2[key] = {
|
|
159136
159030
|
recordKind,
|
|
159137
159031
|
recordId,
|
|
159138
159032
|
contentHash: snapshot.contentHash,
|
|
159139
159033
|
data: changed.data,
|
|
159140
159034
|
file: records2[key]?.file,
|
|
159141
|
-
projectBinary: projectBinary
|
|
159035
|
+
projectBinary: records2[key]?.projectBinary
|
|
159142
159036
|
};
|
|
159143
159037
|
}
|
|
159144
159038
|
return records2;
|
|
@@ -159199,7 +159093,7 @@ function rewriteFilesFromState(workspace, records2 = new Map(
|
|
|
159199
159093
|
data: recordState.data
|
|
159200
159094
|
}
|
|
159201
159095
|
])
|
|
159202
|
-
), preservedSourceFiles = /* @__PURE__ */ new Map(), recordFiles = /* @__PURE__ */ new Map(), localValues = workspace.state.localValues, sourceSnapshot) {
|
|
159096
|
+
), preservedSourceFiles = /* @__PURE__ */ new Map(), recordFiles = /* @__PURE__ */ new Map(), localValues = workspace.state.localValues, sourceSnapshot, binaryFiles) {
|
|
159203
159097
|
const result = emitProjectDocumentSourcesV4(
|
|
159204
159098
|
candidateValueRecordsForEmissionV4(
|
|
159205
159099
|
records2,
|
|
@@ -159209,7 +159103,7 @@ function rewriteFilesFromState(workspace, records2 = new Map(
|
|
|
159209
159103
|
{
|
|
159210
159104
|
recordFiles,
|
|
159211
159105
|
localValues,
|
|
159212
|
-
binaryPaths: workspaceBinaryPathsV4(workspace)
|
|
159106
|
+
binaryPaths: workspaceBinaryPathsV4(workspace, binaryFiles)
|
|
159213
159107
|
}
|
|
159214
159108
|
);
|
|
159215
159109
|
workspace.state.localValues = localValues;
|
|
@@ -172920,7 +172814,7 @@ import {
|
|
|
172920
172814
|
writeFileSync as writeFileSync15
|
|
172921
172815
|
} from "node:fs";
|
|
172922
172816
|
import {
|
|
172923
|
-
basename as
|
|
172817
|
+
basename as basename3,
|
|
172924
172818
|
dirname as dirname12,
|
|
172925
172819
|
isAbsolute as isAbsolute3,
|
|
172926
172820
|
join as join20,
|
|
@@ -173364,7 +173258,7 @@ function selectedSpecPaths(workspace, selectors) {
|
|
|
173364
173258
|
for (const selector of normalizedSelectors) {
|
|
173365
173259
|
if (selector.includes("/") || /[*?]/u.test(selector)) continue;
|
|
173366
173260
|
const basenameMatches = [...relativePaths.values()].filter(
|
|
173367
|
-
(path) =>
|
|
173261
|
+
(path) => basename3(path) === selector
|
|
173368
173262
|
);
|
|
173369
173263
|
if (basenameMatches.length > 1) {
|
|
173370
173264
|
throw new Error(
|
|
@@ -173373,7 +173267,7 @@ function selectedSpecPaths(workspace, selectors) {
|
|
|
173373
173267
|
}
|
|
173374
173268
|
}
|
|
173375
173269
|
const configuredIncludes = workspace.config.test?.include ?? [];
|
|
173376
|
-
const selectorMatches = (selector, path) => path === selector ||
|
|
173270
|
+
const selectorMatches = (selector, path) => path === selector || basename3(path) === selector || path.startsWith(`${selector.replace(/\/$/u, "")}/`) || globMatches(selector, path);
|
|
173377
173271
|
const selectedPaths = all.filter((absolutePath) => {
|
|
173378
173272
|
const path = relativePaths.get(absolutePath);
|
|
173379
173273
|
const configured = configuredIncludes.length === 0 || configuredIncludes.some((pattern) => globMatches(pattern, path));
|
|
@@ -174135,14 +174029,14 @@ function maintainNeoTestBuildCache(root, maxBytes = TEST_BUILD_CACHE_LIMIT_BYTES
|
|
|
174135
174029
|
return fromProtected === "" || !fromProtected.startsWith(`..${sep9}`) && fromProtected !== ".." && !isAbsolute3(fromProtected);
|
|
174136
174030
|
};
|
|
174137
174031
|
for (const file of testBuildFiles(root)) {
|
|
174138
|
-
if (!isProtected(file.path) &&
|
|
174032
|
+
if (!isProtected(file.path) && basename3(file.path).includes(".tmp-") && now - file.modifiedMs >= ABANDONED_TEMP_MAX_AGE_MS) {
|
|
174139
174033
|
rmSync10(file.path, { force: true });
|
|
174140
174034
|
}
|
|
174141
174035
|
}
|
|
174142
174036
|
const files = testBuildFiles(root);
|
|
174143
174037
|
let total = files.reduce((sum, file) => sum + file.size, 0);
|
|
174144
174038
|
const removable = files.filter(
|
|
174145
|
-
(file) =>
|
|
174039
|
+
(file) => basename3(file.path) !== "manifest.json" && !isProtected(file.path)
|
|
174146
174040
|
).sort((left, right) => left.modifiedMs - right.modifiedMs);
|
|
174147
174041
|
for (const file of removable) {
|
|
174148
174042
|
if (total <= maxBytes) break;
|
|
@@ -180887,7 +180781,7 @@ __export(dev_exports, {
|
|
|
180887
180781
|
runDev: () => runDev
|
|
180888
180782
|
});
|
|
180889
180783
|
import { watch } from "node:fs";
|
|
180890
|
-
import { basename as
|
|
180784
|
+
import { basename as basename4, dirname as dirname13 } from "node:path";
|
|
180891
180785
|
import { emitKeypressEvents } from "node:readline";
|
|
180892
180786
|
async function runDev(loadWorkspace2, options) {
|
|
180893
180787
|
const root = loadWorkspace2().root;
|
|
@@ -180924,7 +180818,7 @@ async function runDev(loadWorkspace2, options) {
|
|
|
180924
180818
|
const watchConfigAsset = (path) => {
|
|
180925
180819
|
if (configWatch?.path === path) return;
|
|
180926
180820
|
configWatch?.watcher.close();
|
|
180927
|
-
const name =
|
|
180821
|
+
const name = basename4(path);
|
|
180928
180822
|
const watcher = watch(dirname13(path), (_event, filename) => {
|
|
180929
180823
|
if (filename === name) schedule();
|
|
180930
180824
|
});
|
|
@@ -181391,7 +181285,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
|
|
|
181391
181285
|
async function main() {
|
|
181392
181286
|
const args = parseArgs(process.argv.slice(2));
|
|
181393
181287
|
if (args.command === "--version") {
|
|
181394
|
-
console.log("0.70.
|
|
181288
|
+
console.log("0.70.2");
|
|
181395
181289
|
return;
|
|
181396
181290
|
}
|
|
181397
181291
|
if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ description: >-
|
|
|
9
9
|
`@neocompose/cli` or `node cli/bin/neo.mjs` in the neo-compose repository.
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
<!-- reviewed-through-cli: 0.70.
|
|
12
|
+
<!-- reviewed-through-cli: 0.70.2 -->
|
|
13
13
|
|
|
14
14
|
# Neo Compose CLI
|
|
15
15
|
|
|
@@ -237,7 +237,9 @@ Registry declarations and their image/audio paths may be in any project folder
|
|
|
237
237
|
outside ignored/private directories. Update the registry path when moving a
|
|
238
238
|
binary file. Renaming the registry symbol or replacing bytes retains the file
|
|
239
239
|
ID when the declaration keeps its `@id`; keep the symbol aligned with the
|
|
240
|
-
persisted file name, or set `@settings(name: ...)` explicitly.
|
|
240
|
+
persisted file name, or set `@settings(name: ...)` explicitly. The name must
|
|
241
|
+
end with the binary's extension, as in `@settings(name: "ClinkingCoin1.mp3")`,
|
|
242
|
+
because pull rebuilds the file's path from it.
|
|
241
243
|
Omitting `@settings(template: ...)` uses the complete built-in Unity import
|
|
242
244
|
defaults for that file kind.
|
|
243
245
|
|
|
@@ -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.70.
|
|
113
|
+
<!-- reviewed-through-cli: 0.70.2 -->
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
The quoted version above is checked too, so this instruction cannot go stale
|
|
@@ -263,7 +263,8 @@ Registry symbols derive deterministically from the persisted file name. For
|
|
|
263
263
|
example, `tree.cherryblossom.png` becomes `TreeCherryblossom`. Keep an authored
|
|
264
264
|
declaration aligned with that spelling, or set an explicit persisted name such
|
|
265
265
|
as `@settings(name: "TreeCherryBlossom.png")` when the binary path should stay
|
|
266
|
-
different.
|
|
266
|
+
different. The name must end with the binary's extension, because pull
|
|
267
|
+
rebuilds the file's path from it. With no template annotation, the CLI persists the complete built-in
|
|
267
268
|
Unity defaults for the file kind.
|
|
268
269
|
|
|
269
270
|
Pull/push compare server-verified SHA-256, not storage ETags. A divergent
|