@neocompose/cli 0.22.0 → 0.22.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,27 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.22.2] - 2026-08-04
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Dependency-selected NeoScript preparation narrows source-only bodies by
|
|
8
|
+
recoverable changed-contract identifiers instead of recompiling every body
|
|
9
|
+
without stored IR. Structurally affected owners and source that cannot be
|
|
10
|
+
lexically recovered retain the conservative fallback.
|
|
11
|
+
- Optimized-versus-exhaustive fuzz parity now covers class, enum, value,
|
|
12
|
+
dialogue-node, and dialogue-group edits in addition to member edits.
|
|
13
|
+
- Normal `neo pull` persists a transaction cursor and downloads only records
|
|
14
|
+
changed since the previous sync. Source-neutral deltas update local state
|
|
15
|
+
without rebuilding the complete canonical project source.
|
|
16
|
+
|
|
17
|
+
## [0.22.1] - 2026-08-04
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- The published `neo` executable retains its Node.js shebang, and package
|
|
22
|
+
verification checks the packed bin target and invokes it directly before
|
|
23
|
+
release.
|
|
24
|
+
|
|
3
25
|
## [0.22.0] - 2026-08-04
|
|
4
26
|
|
|
5
27
|
### Added
|
package/README.md
CHANGED
|
@@ -92,7 +92,10 @@ neo init --project <id> --version <id> --dir neo
|
|
|
92
92
|
|
|
93
93
|
For an existing format-4 workspace, `neo pull --reset` regenerates canonical
|
|
94
94
|
source and managed binaries. Normal `neo pull` performs a stable-ID-aware
|
|
95
|
-
three-way merge
|
|
95
|
+
three-way merge, preserves source placement when possible, and uses a durable
|
|
96
|
+
transaction cursor to fetch only records changed since the previous sync.
|
|
97
|
+
Undo/reset metadata invalidates that cursor and safely falls back to a full
|
|
98
|
+
snapshot.
|
|
96
99
|
|
|
97
100
|
In Unity projects, `neo.json` may use `unityConfigPath` instead of storing
|
|
98
101
|
`projectId` and `versionId`. The referenced `NeoComposeConfig.asset` then owns
|
package/dist/neo.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
4
|
var __esm = (fn, res, err) => function __init() {
|
|
@@ -68315,13 +68316,21 @@ function collectNeoScriptRecompileTargets(args) {
|
|
|
68315
68316
|
}
|
|
68316
68317
|
}
|
|
68317
68318
|
const constructorOwnerById = constructorOwners(args.postDocument);
|
|
68319
|
+
const impactedSourceNames = collectImpactedSourceNames({
|
|
68320
|
+
currentDocument: args.currentDocument,
|
|
68321
|
+
postDocument: args.postDocument,
|
|
68322
|
+
impactedIds,
|
|
68323
|
+
impactedTypeNames,
|
|
68324
|
+
constructedClassIds
|
|
68325
|
+
});
|
|
68318
68326
|
const memberIds = new Set(explicit.memberIds);
|
|
68319
68327
|
for (const member of args.postDocument.members) {
|
|
68320
68328
|
if (recordDependsOnChangedContract(
|
|
68321
68329
|
member,
|
|
68322
68330
|
impactedIds,
|
|
68323
68331
|
impactedTypeNames,
|
|
68324
|
-
constructedClassIds
|
|
68332
|
+
constructedClassIds,
|
|
68333
|
+
impactedSourceNames
|
|
68325
68334
|
)) {
|
|
68326
68335
|
memberIds.add(member.id);
|
|
68327
68336
|
}
|
|
@@ -68332,7 +68341,8 @@ function collectNeoScriptRecompileTargets(args) {
|
|
|
68332
68341
|
explicit.valueIds,
|
|
68333
68342
|
impactedIds,
|
|
68334
68343
|
impactedTypeNames,
|
|
68335
|
-
constructedClassIds
|
|
68344
|
+
constructedClassIds,
|
|
68345
|
+
impactedSourceNames
|
|
68336
68346
|
);
|
|
68337
68347
|
if (impactedIds.size > 0 && postValues.length > 0) {
|
|
68338
68348
|
const ownerByValueId = resolveOwnerMembersForValues(
|
|
@@ -68355,6 +68365,7 @@ function collectNeoScriptRecompileTargets(args) {
|
|
|
68355
68365
|
impactedIds,
|
|
68356
68366
|
impactedTypeNames,
|
|
68357
68367
|
constructedClassIds,
|
|
68368
|
+
impactedSourceNames,
|
|
68358
68369
|
(record3) => constructorOwnerById.get(record3.id) ?? null
|
|
68359
68370
|
),
|
|
68360
68371
|
migrationIds: selectedRecordIds(
|
|
@@ -68362,21 +68373,24 @@ function collectNeoScriptRecompileTargets(args) {
|
|
|
68362
68373
|
explicit.migrationIds,
|
|
68363
68374
|
impactedIds,
|
|
68364
68375
|
impactedTypeNames,
|
|
68365
|
-
constructedClassIds
|
|
68376
|
+
constructedClassIds,
|
|
68377
|
+
impactedSourceNames
|
|
68366
68378
|
),
|
|
68367
68379
|
dialogueNodeIds: selectedRecordIds(
|
|
68368
68380
|
args.postDocument.dialogueNodes ?? [],
|
|
68369
68381
|
explicit.dialogueNodeIds,
|
|
68370
68382
|
impactedIds,
|
|
68371
68383
|
impactedTypeNames,
|
|
68372
|
-
constructedClassIds
|
|
68384
|
+
constructedClassIds,
|
|
68385
|
+
impactedSourceNames
|
|
68373
68386
|
),
|
|
68374
68387
|
dialogueGroupIds: selectedRecordIds(
|
|
68375
68388
|
args.postDocument.dialogueGroups ?? [],
|
|
68376
68389
|
explicit.dialogueGroupIds,
|
|
68377
68390
|
impactedIds,
|
|
68378
68391
|
impactedTypeNames,
|
|
68379
|
-
constructedClassIds
|
|
68392
|
+
constructedClassIds,
|
|
68393
|
+
impactedSourceNames
|
|
68380
68394
|
),
|
|
68381
68395
|
valueIds
|
|
68382
68396
|
};
|
|
@@ -68745,7 +68759,7 @@ function explicitTargetIds(changes) {
|
|
|
68745
68759
|
}
|
|
68746
68760
|
return result;
|
|
68747
68761
|
}
|
|
68748
|
-
function selectedRecordIds(records2, explicitIds, impactedIds, impactedTypeNames, constructedClassIds, ownerId = () => null) {
|
|
68762
|
+
function selectedRecordIds(records2, explicitIds, impactedIds, impactedTypeNames, constructedClassIds, impactedSourceNames, ownerId = () => null) {
|
|
68749
68763
|
const selected2 = new Set(explicitIds);
|
|
68750
68764
|
for (const record3 of records2) {
|
|
68751
68765
|
const owner = ownerId(record3);
|
|
@@ -68753,14 +68767,17 @@ function selectedRecordIds(records2, explicitIds, impactedIds, impactedTypeNames
|
|
|
68753
68767
|
record3,
|
|
68754
68768
|
impactedIds,
|
|
68755
68769
|
impactedTypeNames,
|
|
68756
|
-
constructedClassIds
|
|
68770
|
+
constructedClassIds,
|
|
68771
|
+
impactedSourceNames
|
|
68757
68772
|
)) {
|
|
68758
68773
|
selected2.add(record3.id);
|
|
68759
68774
|
}
|
|
68760
68775
|
}
|
|
68761
68776
|
return selected2;
|
|
68762
68777
|
}
|
|
68763
|
-
function recordDependsOnChangedContract(record3, impactedIds, impactedTypeNames, constructedClassIds) {
|
|
68778
|
+
function recordDependsOnChangedContract(record3, impactedIds, impactedTypeNames, constructedClassIds, impactedSourceNames) {
|
|
68779
|
+
const recordId = isObjectRecord3(record3) ? record3.id : void 0;
|
|
68780
|
+
if (typeof recordId === "string" && impactedIds.has(recordId)) return true;
|
|
68764
68781
|
if (intersects(collectCompilerReferenceIds(record3), impactedIds)) return true;
|
|
68765
68782
|
if (intersects(collectCompilerReferenceNames(record3), impactedTypeNames)) {
|
|
68766
68783
|
return true;
|
|
@@ -68768,7 +68785,109 @@ function recordDependsOnChangedContract(record3, impactedIds, impactedTypeNames,
|
|
|
68768
68785
|
if (intersects(collectConstructedClassIds(record3), constructedClassIds)) {
|
|
68769
68786
|
return true;
|
|
68770
68787
|
}
|
|
68771
|
-
|
|
68788
|
+
if (impactedIds.size === 0 && impactedTypeNames.size === 0) return false;
|
|
68789
|
+
if (!containsAuthoredNeoScriptSource(record3)) return false;
|
|
68790
|
+
if (containsCompiledNeoScriptBody(record3)) return false;
|
|
68791
|
+
if (impactedSourceNames === null) return true;
|
|
68792
|
+
const sourceIdentifiers = collectAuthoredNeoScriptSourceIdentifiers(record3);
|
|
68793
|
+
if (sourceIdentifiers === null) return true;
|
|
68794
|
+
return intersects(sourceIdentifiers, impactedSourceNames);
|
|
68795
|
+
}
|
|
68796
|
+
function collectImpactedSourceNames(args) {
|
|
68797
|
+
const names = new Set(args.impactedTypeNames);
|
|
68798
|
+
let recoveredContract = args.impactedTypeNames.size > 0;
|
|
68799
|
+
const collectDocument = (document) => {
|
|
68800
|
+
const classById = new Map(
|
|
68801
|
+
document.classes.map((schemaClass2) => [schemaClass2.id, schemaClass2])
|
|
68802
|
+
);
|
|
68803
|
+
const ownerByConstructorId = constructorOwners(document);
|
|
68804
|
+
const impactedValueIds = new Set(
|
|
68805
|
+
(document.values ?? []).filter((value) => args.impactedIds.has(value.id)).map((value) => value.id)
|
|
68806
|
+
);
|
|
68807
|
+
const ownerByValueId = resolveOwnerMembersForValues(
|
|
68808
|
+
document,
|
|
68809
|
+
impactedValueIds
|
|
68810
|
+
);
|
|
68811
|
+
for (const owner of ownerByValueId.values()) {
|
|
68812
|
+
recoveredContract = true;
|
|
68813
|
+
collectDeclaredSourceNames(owner, names);
|
|
68814
|
+
const ownerId = Reflect.get(owner, "id");
|
|
68815
|
+
if (typeof ownerId !== "string") continue;
|
|
68816
|
+
for (const classId of classPlacementsForMember(ownerId, document)) {
|
|
68817
|
+
const schemaClass2 = classById.get(classId);
|
|
68818
|
+
if (schemaClass2 !== void 0) {
|
|
68819
|
+
collectDeclaredSourceNames(schemaClass2, names);
|
|
68820
|
+
}
|
|
68821
|
+
}
|
|
68822
|
+
}
|
|
68823
|
+
const records2 = [
|
|
68824
|
+
...document.classes,
|
|
68825
|
+
...document.members,
|
|
68826
|
+
...document.constructors ?? [],
|
|
68827
|
+
...document.enums,
|
|
68828
|
+
...document.interfaces ?? [],
|
|
68829
|
+
...document.values ?? []
|
|
68830
|
+
];
|
|
68831
|
+
for (const record3 of records2) {
|
|
68832
|
+
if (!args.impactedIds.has(record3.id)) continue;
|
|
68833
|
+
recoveredContract = true;
|
|
68834
|
+
collectDeclaredSourceNames(record3, names);
|
|
68835
|
+
const ownerClassId = ownerByConstructorId.get(record3.id);
|
|
68836
|
+
if (ownerClassId === void 0) continue;
|
|
68837
|
+
const owner = classById.get(ownerClassId);
|
|
68838
|
+
if (owner !== void 0) collectDeclaredSourceNames(owner, names);
|
|
68839
|
+
}
|
|
68840
|
+
for (const schemaClass2 of document.classes) {
|
|
68841
|
+
if (!args.constructedClassIds.has(schemaClass2.id)) continue;
|
|
68842
|
+
recoveredContract = true;
|
|
68843
|
+
collectDeclaredSourceNames(schemaClass2, names);
|
|
68844
|
+
}
|
|
68845
|
+
};
|
|
68846
|
+
collectDocument(args.currentDocument);
|
|
68847
|
+
collectDocument(args.postDocument);
|
|
68848
|
+
return recoveredContract ? names : null;
|
|
68849
|
+
}
|
|
68850
|
+
function collectDeclaredSourceNames(value, names) {
|
|
68851
|
+
if (Array.isArray(value)) {
|
|
68852
|
+
for (const child of value) collectDeclaredSourceNames(child, names);
|
|
68853
|
+
return;
|
|
68854
|
+
}
|
|
68855
|
+
if (!isObjectRecord3(value)) return;
|
|
68856
|
+
for (const [field, child] of Object.entries(value)) {
|
|
68857
|
+
if (typeof child === "string" && (field === "name" || field === "sourceName" || field === "sourceFunctionName" || field === "declaredTypeName" || field === "typeName")) {
|
|
68858
|
+
names.add(child);
|
|
68859
|
+
continue;
|
|
68860
|
+
}
|
|
68861
|
+
collectDeclaredSourceNames(child, names);
|
|
68862
|
+
}
|
|
68863
|
+
}
|
|
68864
|
+
function collectAuthoredNeoScriptSourceIdentifiers(value) {
|
|
68865
|
+
const identifiers = /* @__PURE__ */ new Set();
|
|
68866
|
+
let complete2 = true;
|
|
68867
|
+
const collect = (child) => {
|
|
68868
|
+
if (Array.isArray(child)) {
|
|
68869
|
+
for (const entry of child) collect(entry);
|
|
68870
|
+
return;
|
|
68871
|
+
}
|
|
68872
|
+
if (!isObjectRecord3(child)) return;
|
|
68873
|
+
for (const [field, nested] of Object.entries(child)) {
|
|
68874
|
+
if ((field === "code" || field === "setterCode") && typeof nested === "string") {
|
|
68875
|
+
const result = lex(nested);
|
|
68876
|
+
if (result.diagnostics.some(
|
|
68877
|
+
(diagnostic) => diagnostic.severity === "error"
|
|
68878
|
+
)) {
|
|
68879
|
+
complete2 = false;
|
|
68880
|
+
}
|
|
68881
|
+
for (const token of result.tokens) {
|
|
68882
|
+
if (token.kind === "identifier") identifiers.add(token.text);
|
|
68883
|
+
}
|
|
68884
|
+
continue;
|
|
68885
|
+
}
|
|
68886
|
+
collect(nested);
|
|
68887
|
+
}
|
|
68888
|
+
};
|
|
68889
|
+
collect(value);
|
|
68890
|
+
return complete2 ? identifiers : null;
|
|
68772
68891
|
}
|
|
68773
68892
|
function containsAuthoredNeoScriptSource(value) {
|
|
68774
68893
|
if (Array.isArray(value)) {
|
|
@@ -68867,6 +68986,7 @@ var init_neo_script_recompile_scope = __esm({
|
|
|
68867
68986
|
"../src/database/neo-script-recompile-scope.ts"() {
|
|
68868
68987
|
"use strict";
|
|
68869
68988
|
init_canonical_json();
|
|
68989
|
+
init_src();
|
|
68870
68990
|
init_member_kind_enum();
|
|
68871
68991
|
init_inheritance();
|
|
68872
68992
|
init_effective_storage();
|
|
@@ -87198,20 +87318,172 @@ async function fetchProjectDocument(workspace) {
|
|
|
87198
87318
|
});
|
|
87199
87319
|
}
|
|
87200
87320
|
}
|
|
87321
|
+
const revision = readProjectDocumentRevisionMarker2(raw);
|
|
87201
87322
|
return {
|
|
87202
87323
|
records: records2,
|
|
87203
87324
|
heads,
|
|
87204
|
-
raw
|
|
87325
|
+
raw,
|
|
87326
|
+
...revision === null ? {} : { revision }
|
|
87327
|
+
};
|
|
87328
|
+
}
|
|
87329
|
+
async function fetchProjectDocumentSyncMarker(workspace) {
|
|
87330
|
+
const convex = await createConvexClient(workspace);
|
|
87331
|
+
return queryProjectDocumentSyncMarker(workspace, convex);
|
|
87332
|
+
}
|
|
87333
|
+
async function queryProjectDocumentSyncMarker(workspace, convex) {
|
|
87334
|
+
const [revisionValue, signal] = await Promise.all([
|
|
87335
|
+
convex.query(api2.projectDocuments.getDocumentRevision, {
|
|
87336
|
+
projectId: workspace.config.projectId,
|
|
87337
|
+
versionId: workspace.config.versionId
|
|
87338
|
+
}),
|
|
87339
|
+
convex.query(api2.projectExportData.schemaSignal, {
|
|
87340
|
+
projectId: workspace.config.projectId,
|
|
87341
|
+
versionId: workspace.config.versionId
|
|
87342
|
+
})
|
|
87343
|
+
]);
|
|
87344
|
+
if (!isProjectDocumentRevisionMarker(revisionValue)) {
|
|
87345
|
+
throw new Error(
|
|
87346
|
+
"Project document revision query returned an invalid cursor marker."
|
|
87347
|
+
);
|
|
87348
|
+
}
|
|
87349
|
+
return {
|
|
87350
|
+
revision: revisionValue,
|
|
87351
|
+
headTransactionId: signal?.transactionId ?? null,
|
|
87352
|
+
headTransactionHash: signal?.transactionHash ?? null
|
|
87353
|
+
};
|
|
87354
|
+
}
|
|
87355
|
+
async function fetchProjectDocumentDelta(workspace, cursor) {
|
|
87356
|
+
const convex = await createConvexClient(workspace);
|
|
87357
|
+
const marker = await queryProjectDocumentSyncMarker(workspace, convex);
|
|
87358
|
+
if (marker.revision.versionsStamp !== cursor.versionsStamp) {
|
|
87359
|
+
return {
|
|
87360
|
+
fullResync: true,
|
|
87361
|
+
headTransactionHash: marker.headTransactionHash
|
|
87362
|
+
};
|
|
87363
|
+
}
|
|
87364
|
+
if (marker.headTransactionId === marker.revision.latestTransactionId && marker.headTransactionHash === workspace.state.headTransactionHash) {
|
|
87365
|
+
return {
|
|
87366
|
+
fullResync: false,
|
|
87367
|
+
document: projectDocumentFromWorkspaceState(workspace.state, cursor),
|
|
87368
|
+
changedKeys: /* @__PURE__ */ new Set(),
|
|
87369
|
+
cursor,
|
|
87370
|
+
headTransactionHash: marker.headTransactionHash
|
|
87371
|
+
};
|
|
87372
|
+
}
|
|
87373
|
+
const result = await convex.query(api2.projectDocuments.getRecordsSince, {
|
|
87374
|
+
projectId: workspace.config.projectId,
|
|
87375
|
+
versionId: workspace.config.versionId,
|
|
87376
|
+
sinceCreatedAt: cursor.createdAt,
|
|
87377
|
+
seenTransactionIds: [...cursor.transactionIds]
|
|
87378
|
+
});
|
|
87379
|
+
if (result.fullResync) {
|
|
87380
|
+
return {
|
|
87381
|
+
fullResync: true,
|
|
87382
|
+
headTransactionHash: marker.headTransactionHash
|
|
87383
|
+
};
|
|
87384
|
+
}
|
|
87385
|
+
const document = projectDocumentFromWorkspaceState(workspace.state, cursor);
|
|
87386
|
+
const { records: records2, heads } = document;
|
|
87387
|
+
const changedKeys = /* @__PURE__ */ new Set();
|
|
87388
|
+
for (const record3 of result.records) {
|
|
87389
|
+
const key = `${record3.recordKind}:${record3.recordId}`;
|
|
87390
|
+
changedKeys.add(key);
|
|
87391
|
+
if (record3.deleted) {
|
|
87392
|
+
records2.delete(key);
|
|
87393
|
+
heads.set(key, {
|
|
87394
|
+
recordKind: record3.recordKind,
|
|
87395
|
+
recordId: record3.recordId,
|
|
87396
|
+
contentHash: "",
|
|
87397
|
+
deleted: true
|
|
87398
|
+
});
|
|
87399
|
+
continue;
|
|
87400
|
+
}
|
|
87401
|
+
if (record3.contentHash === null) {
|
|
87402
|
+
throw new Error(
|
|
87403
|
+
`Project document delta record "${key}" is live but has no content hash.`
|
|
87404
|
+
);
|
|
87405
|
+
}
|
|
87406
|
+
records2.set(key, {
|
|
87407
|
+
recordKind: record3.recordKind,
|
|
87408
|
+
recordId: record3.recordId,
|
|
87409
|
+
contentHash: record3.contentHash,
|
|
87410
|
+
deleted: false,
|
|
87411
|
+
data: record3.data
|
|
87412
|
+
});
|
|
87413
|
+
heads.set(key, {
|
|
87414
|
+
recordKind: record3.recordKind,
|
|
87415
|
+
recordId: record3.recordId,
|
|
87416
|
+
contentHash: record3.contentHash,
|
|
87417
|
+
deleted: false
|
|
87418
|
+
});
|
|
87419
|
+
}
|
|
87420
|
+
const next = result.cursor ?? cursor;
|
|
87421
|
+
const nextCursor = {
|
|
87422
|
+
createdAt: next.createdAt,
|
|
87423
|
+
transactionIds: [...next.transactionIds],
|
|
87424
|
+
versionsStamp: marker.revision.versionsStamp
|
|
87425
|
+
};
|
|
87426
|
+
return {
|
|
87427
|
+
fullResync: false,
|
|
87428
|
+
document: projectDocumentWithRevision(document, nextCursor),
|
|
87429
|
+
changedKeys,
|
|
87430
|
+
cursor: nextCursor,
|
|
87431
|
+
headTransactionHash: marker.headTransactionHash
|
|
87432
|
+
};
|
|
87433
|
+
}
|
|
87434
|
+
function projectDocumentFromWorkspaceState(state, cursor) {
|
|
87435
|
+
const records2 = serverRecordsFromWorkspaceState(state);
|
|
87436
|
+
const heads = new Map(
|
|
87437
|
+
[...records2].map(([key, record3]) => [
|
|
87438
|
+
key,
|
|
87439
|
+
{
|
|
87440
|
+
recordKind: record3.recordKind,
|
|
87441
|
+
recordId: record3.recordId,
|
|
87442
|
+
contentHash: record3.contentHash,
|
|
87443
|
+
deleted: false
|
|
87444
|
+
}
|
|
87445
|
+
])
|
|
87446
|
+
);
|
|
87447
|
+
return projectDocumentWithRevision({ records: records2, heads, raw: {} }, cursor);
|
|
87448
|
+
}
|
|
87449
|
+
function projectDocumentWithRevision(document, cursor) {
|
|
87450
|
+
return {
|
|
87451
|
+
...document,
|
|
87452
|
+
revision: {
|
|
87453
|
+
latestTransactionId: cursor.transactionIds[cursor.transactionIds.length - 1] ?? null,
|
|
87454
|
+
latestTransactionCreatedAt: cursor.createdAt,
|
|
87455
|
+
versionsStamp: cursor.versionsStamp
|
|
87456
|
+
}
|
|
87205
87457
|
};
|
|
87206
87458
|
}
|
|
87207
|
-
|
|
87459
|
+
function serverRecordsFromWorkspaceState(state) {
|
|
87460
|
+
const records2 = /* @__PURE__ */ new Map();
|
|
87461
|
+
for (const [key, record3] of Object.entries(state.records)) {
|
|
87462
|
+
if (record3.conflictServerHash === null) continue;
|
|
87463
|
+
const conflictServerHash = record3.conflictServerHash;
|
|
87464
|
+
const hasConflictServer = conflictServerHash !== void 0;
|
|
87465
|
+
records2.set(key, {
|
|
87466
|
+
recordKind: record3.recordKind,
|
|
87467
|
+
recordId: record3.recordId,
|
|
87468
|
+
contentHash: hasConflictServer ? conflictServerHash : record3.contentHash,
|
|
87469
|
+
deleted: false,
|
|
87470
|
+
data: hasConflictServer ? record3.conflictServerData : record3.data
|
|
87471
|
+
});
|
|
87472
|
+
}
|
|
87473
|
+
return records2;
|
|
87474
|
+
}
|
|
87475
|
+
var fetchProjectDocumentRawChunked2, readProjectDocumentContentHashHeads2, readProjectDocumentRevisionMarker2, SOURCE_RECORD_KINDS;
|
|
87208
87476
|
var init_document = __esm({
|
|
87209
87477
|
"src/document.ts"() {
|
|
87210
87478
|
"use strict";
|
|
87211
87479
|
init_project_document_read();
|
|
87212
87480
|
init_project2();
|
|
87213
87481
|
init_convex();
|
|
87214
|
-
({
|
|
87482
|
+
({
|
|
87483
|
+
fetchProjectDocumentRawChunked: fetchProjectDocumentRawChunked2,
|
|
87484
|
+
readProjectDocumentContentHashHeads: readProjectDocumentContentHashHeads2,
|
|
87485
|
+
readProjectDocumentRevisionMarker: readProjectDocumentRevisionMarker2
|
|
87486
|
+
} = project_document_read_exports);
|
|
87215
87487
|
SOURCE_RECORD_KINDS = new Set(
|
|
87216
87488
|
Object.values(ProjectRecordKind)
|
|
87217
87489
|
);
|
|
@@ -87898,6 +88170,7 @@ var init_static_value_seed_records = __esm({
|
|
|
87898
88170
|
// src/commands/pull.ts
|
|
87899
88171
|
var pull_exports = {};
|
|
87900
88172
|
__export(pull_exports, {
|
|
88173
|
+
deltaRequiresSourceProjectionV4: () => deltaRequiresSourceProjectionV4,
|
|
87901
88174
|
projectSourcePathsRequiringRewriteV4: () => projectSourcePathsRequiringRewriteV4,
|
|
87902
88175
|
runPull: () => runPull
|
|
87903
88176
|
});
|
|
@@ -87914,6 +88187,15 @@ async function runPull(workspace, options) {
|
|
|
87914
88187
|
await runResetPull(workspace);
|
|
87915
88188
|
return;
|
|
87916
88189
|
}
|
|
88190
|
+
const progress = spinner("Checking local workspace\u2026");
|
|
88191
|
+
try {
|
|
88192
|
+
await runNormalPull(workspace, options, progress);
|
|
88193
|
+
} catch (error) {
|
|
88194
|
+
progress.fail("Pull failed.");
|
|
88195
|
+
throw error;
|
|
88196
|
+
}
|
|
88197
|
+
}
|
|
88198
|
+
async function runNormalPull(workspace, options, progress) {
|
|
87917
88199
|
const destructive = options.force;
|
|
87918
88200
|
const hasBaseline = Object.keys(workspace.state.records).length > 0;
|
|
87919
88201
|
let localByKey = /* @__PURE__ */ new Map();
|
|
@@ -87942,25 +88224,54 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
|
|
|
87942
88224
|
])
|
|
87943
88225
|
);
|
|
87944
88226
|
}
|
|
87945
|
-
|
|
87946
|
-
|
|
87947
|
-
|
|
87948
|
-
|
|
87949
|
-
|
|
87950
|
-
|
|
87951
|
-
|
|
87952
|
-
|
|
87953
|
-
|
|
87954
|
-
|
|
87955
|
-
|
|
87956
|
-
|
|
87957
|
-
|
|
87958
|
-
|
|
88227
|
+
let changedRecordKeys = null;
|
|
88228
|
+
let nextCursor = null;
|
|
88229
|
+
let observedHeadTransactionHash;
|
|
88230
|
+
let document = null;
|
|
88231
|
+
if (!destructive && hasBaseline) {
|
|
88232
|
+
progress.update("Checking remote changes\u2026");
|
|
88233
|
+
const cursor = workspace.state.documentRevisionCursor;
|
|
88234
|
+
if (cursor !== void 0) {
|
|
88235
|
+
const delta = await fetchProjectDocumentDelta(workspace, cursor);
|
|
88236
|
+
observedHeadTransactionHash = delta.headTransactionHash;
|
|
88237
|
+
if (!delta.fullResync) {
|
|
88238
|
+
document = delta.document;
|
|
88239
|
+
changedRecordKeys = delta.changedKeys;
|
|
88240
|
+
nextCursor = delta.cursor;
|
|
88241
|
+
if (delta.changedKeys.size === 0 && !options.regenerateSourceNames) {
|
|
88242
|
+
const cursorChanged = !cursorsEqual(
|
|
88243
|
+
workspace.state.documentRevisionCursor,
|
|
88244
|
+
delta.cursor
|
|
88245
|
+
);
|
|
88246
|
+
const headChanged = workspace.state.headTransactionHash !== delta.headTransactionHash;
|
|
88247
|
+
if (cursorChanged || headChanged) {
|
|
88248
|
+
workspace.state.documentRevisionCursor = mutableCursor(
|
|
88249
|
+
delta.cursor
|
|
88250
|
+
);
|
|
88251
|
+
workspace.state.headTransactionHash = delta.headTransactionHash;
|
|
88252
|
+
writeWorkspaceState(workspace.root, workspace.state);
|
|
88253
|
+
}
|
|
88254
|
+
progress.succeed("Already up to date.");
|
|
88255
|
+
return;
|
|
88256
|
+
}
|
|
88257
|
+
}
|
|
88258
|
+
} else if (!options.regenerateSourceNames && workspace.state.headTransactionHash !== void 0) {
|
|
88259
|
+
const marker = await fetchProjectDocumentSyncMarker(workspace);
|
|
88260
|
+
observedHeadTransactionHash = marker.headTransactionHash;
|
|
88261
|
+
if (marker.headTransactionId === marker.revision.latestTransactionId && marker.headTransactionHash === workspace.state.headTransactionHash) {
|
|
88262
|
+
const seededCursor = cursorFromRevision(marker.revision);
|
|
88263
|
+
workspace.state.documentRevisionCursor = mutableCursor(seededCursor);
|
|
88264
|
+
writeWorkspaceState(workspace.root, workspace.state);
|
|
88265
|
+
progress.succeed("Already up to date.");
|
|
87959
88266
|
return;
|
|
87960
88267
|
}
|
|
87961
88268
|
}
|
|
87962
88269
|
}
|
|
87963
|
-
|
|
88270
|
+
if (document === null) {
|
|
88271
|
+
progress.update("Downloading full project snapshot\u2026");
|
|
88272
|
+
document = await fetchProjectDocument(workspace);
|
|
88273
|
+
nextCursor = document.revision === void 0 ? null : cursorFromRevision(document.revision);
|
|
88274
|
+
}
|
|
87964
88275
|
const plans = /* @__PURE__ */ new Map();
|
|
87965
88276
|
let mergedCount = 0;
|
|
87966
88277
|
let conflictCount = 0;
|
|
@@ -88080,7 +88391,11 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
|
|
|
88080
88391
|
conflictKeys,
|
|
88081
88392
|
localStatus,
|
|
88082
88393
|
destructive,
|
|
88083
|
-
regenerateSourceNames: destructive || options.regenerateSourceNames
|
|
88394
|
+
regenerateSourceNames: destructive || options.regenerateSourceNames,
|
|
88395
|
+
changedRecordKeys,
|
|
88396
|
+
nextCursor,
|
|
88397
|
+
observedHeadTransactionHash,
|
|
88398
|
+
progress
|
|
88084
88399
|
});
|
|
88085
88400
|
}
|
|
88086
88401
|
async function runResetPull(workspace) {
|
|
@@ -88110,10 +88425,17 @@ async function runResetPull(workspace) {
|
|
|
88110
88425
|
projectId: workspace.config.projectId,
|
|
88111
88426
|
versionId: workspace.config.versionId
|
|
88112
88427
|
});
|
|
88113
|
-
workspace.state.headTransactionHash = signal?.transactionHash
|
|
88428
|
+
workspace.state.headTransactionHash = document.revision !== void 0 && signal?.transactionId === document.revision.latestTransactionId ? signal.transactionHash : null;
|
|
88114
88429
|
} catch {
|
|
88115
88430
|
workspace.state.headTransactionHash = null;
|
|
88116
88431
|
}
|
|
88432
|
+
if (document.revision !== void 0) {
|
|
88433
|
+
workspace.state.documentRevisionCursor = mutableCursor(
|
|
88434
|
+
cursorFromRevision(document.revision)
|
|
88435
|
+
);
|
|
88436
|
+
} else {
|
|
88437
|
+
delete workspace.state.documentRevisionCursor;
|
|
88438
|
+
}
|
|
88117
88439
|
writeWorkspaceState(workspace.root, workspace.state);
|
|
88118
88440
|
progress.succeed(
|
|
88119
88441
|
`Pulled ${document.records.size} records \u2014 ${reset.written} file(s) updated \u2014 ${reset.removed} removed.`
|
|
@@ -88133,8 +88455,30 @@ async function finishFormat4Pull(args) {
|
|
|
88133
88455
|
conflictKeys,
|
|
88134
88456
|
localStatus,
|
|
88135
88457
|
destructive,
|
|
88136
|
-
regenerateSourceNames
|
|
88458
|
+
regenerateSourceNames,
|
|
88459
|
+
changedRecordKeys,
|
|
88460
|
+
nextCursor,
|
|
88461
|
+
observedHeadTransactionHash,
|
|
88462
|
+
progress
|
|
88137
88463
|
} = args;
|
|
88464
|
+
if (changedRecordKeys !== null && !regenerateSourceNames && conflictCount === 0 && localStatus !== null && !deltaRequiresSourceProjectionV4({
|
|
88465
|
+
workspace,
|
|
88466
|
+
document,
|
|
88467
|
+
plans,
|
|
88468
|
+
localStatus,
|
|
88469
|
+
changedRecordKeys
|
|
88470
|
+
})) {
|
|
88471
|
+
finishRecordOnlyDeltaPull({
|
|
88472
|
+
workspace,
|
|
88473
|
+
document,
|
|
88474
|
+
changedRecordKeys,
|
|
88475
|
+
nextCursor,
|
|
88476
|
+
observedHeadTransactionHash,
|
|
88477
|
+
progress
|
|
88478
|
+
});
|
|
88479
|
+
return;
|
|
88480
|
+
}
|
|
88481
|
+
progress.update("Regenerating affected Neo sources\u2026");
|
|
88138
88482
|
const plannedLocalRecords = buildEmitRecordSet(document, plans, "emit");
|
|
88139
88483
|
for (const [key, record3] of staticValueSeedEmitRecords(
|
|
88140
88484
|
localStatus,
|
|
@@ -88149,7 +88493,7 @@ async function finishFormat4Pull(args) {
|
|
|
88149
88493
|
)
|
|
88150
88494
|
);
|
|
88151
88495
|
const localResult = emitProjectDocumentFilesV4(localRecords);
|
|
88152
|
-
const serverResult = emitProjectDocumentFilesV4(
|
|
88496
|
+
const serverResult = conflictCount === 0 ? null : emitProjectDocumentFilesV4(
|
|
88153
88497
|
buildEmitRecordSet(document, plans, "server")
|
|
88154
88498
|
);
|
|
88155
88499
|
const binaries = await pullProjectBinariesV4({
|
|
@@ -88163,11 +88507,11 @@ async function finishFormat4Pull(args) {
|
|
|
88163
88507
|
localResult.files.map((file) => [file.path, file])
|
|
88164
88508
|
);
|
|
88165
88509
|
const serverByPath = new Map(
|
|
88166
|
-
serverResult
|
|
88510
|
+
(serverResult?.files ?? []).map((file) => [file.path, file])
|
|
88167
88511
|
);
|
|
88168
88512
|
const conflictPaths = /* @__PURE__ */ new Set();
|
|
88169
88513
|
for (const key of conflictKeys) {
|
|
88170
|
-
const path = localResult.recordFiles.get(key) ?? serverResult
|
|
88514
|
+
const path = localResult.recordFiles.get(key) ?? serverResult?.recordFiles.get(key);
|
|
88171
88515
|
if (path) conflictPaths.add(path);
|
|
88172
88516
|
}
|
|
88173
88517
|
const emittedPaths = /* @__PURE__ */ new Set([
|
|
@@ -88179,7 +88523,7 @@ async function finishFormat4Pull(args) {
|
|
|
88179
88523
|
localStatus,
|
|
88180
88524
|
plans,
|
|
88181
88525
|
localRecordFiles: localResult.recordFiles,
|
|
88182
|
-
serverRecordFiles: serverResult
|
|
88526
|
+
serverRecordFiles: serverResult?.recordFiles ?? /* @__PURE__ */ new Map(),
|
|
88183
88527
|
previousRecords: workspace.state.records,
|
|
88184
88528
|
conflictPaths,
|
|
88185
88529
|
emittedPaths,
|
|
@@ -88229,7 +88573,7 @@ async function finishFormat4Pull(args) {
|
|
|
88229
88573
|
if (baseState) records2[key] = { ...baseState };
|
|
88230
88574
|
continue;
|
|
88231
88575
|
}
|
|
88232
|
-
const file = localResult.recordFiles.get(key) ?? serverResult
|
|
88576
|
+
const file = localResult.recordFiles.get(key) ?? serverResult?.recordFiles.get(key) ?? baseState?.file;
|
|
88233
88577
|
records2[key] = plan.conflicted ? {
|
|
88234
88578
|
recordKind,
|
|
88235
88579
|
recordId,
|
|
@@ -88248,7 +88592,7 @@ async function finishFormat4Pull(args) {
|
|
|
88248
88592
|
}
|
|
88249
88593
|
for (const [key, serverRecord] of document.records) {
|
|
88250
88594
|
if (serverRecord.deleted || records2[key]) continue;
|
|
88251
|
-
const file = localResult.recordFiles.get(key) ?? serverResult
|
|
88595
|
+
const file = localResult.recordFiles.get(key) ?? serverResult?.recordFiles.get(key);
|
|
88252
88596
|
records2[key] = {
|
|
88253
88597
|
recordKind: serverRecord.recordKind,
|
|
88254
88598
|
recordId: serverRecord.recordId,
|
|
@@ -88262,30 +88606,37 @@ async function finishFormat4Pull(args) {
|
|
|
88262
88606
|
if (record3) record3.projectBinary = state;
|
|
88263
88607
|
}
|
|
88264
88608
|
workspace.state.records = records2;
|
|
88265
|
-
|
|
88266
|
-
|
|
88267
|
-
|
|
88268
|
-
|
|
88269
|
-
|
|
88270
|
-
|
|
88271
|
-
|
|
88272
|
-
|
|
88273
|
-
|
|
88609
|
+
if (nextCursor !== null) {
|
|
88610
|
+
workspace.state.documentRevisionCursor = mutableCursor(nextCursor);
|
|
88611
|
+
}
|
|
88612
|
+
if (observedHeadTransactionHash !== void 0) {
|
|
88613
|
+
workspace.state.headTransactionHash = observedHeadTransactionHash;
|
|
88614
|
+
} else {
|
|
88615
|
+
try {
|
|
88616
|
+
const convex = await createConvexClient(workspace);
|
|
88617
|
+
const signal = await convex.query(api2.projectExportData.schemaSignal, {
|
|
88618
|
+
projectId: workspace.config.projectId,
|
|
88619
|
+
versionId: workspace.config.versionId
|
|
88620
|
+
});
|
|
88621
|
+
workspace.state.headTransactionHash = document.revision !== void 0 && signal?.transactionId === document.revision.latestTransactionId ? signal.transactionHash : null;
|
|
88622
|
+
} catch {
|
|
88623
|
+
workspace.state.headTransactionHash = null;
|
|
88624
|
+
}
|
|
88274
88625
|
}
|
|
88275
88626
|
writeWorkspaceState(workspace.root, workspace.state);
|
|
88276
88627
|
if (conflictCount === 0) {
|
|
88277
88628
|
writeProjectSourceAnalysisCacheV4(workspace.root, localResult.analysis);
|
|
88278
|
-
|
|
88279
|
-
|
|
88280
|
-
|
|
88281
|
-
|
|
88282
|
-
|
|
88283
|
-
|
|
88284
|
-
|
|
88285
|
-
);
|
|
88629
|
+
if (localStatus === null || localStatus.changes.length === 0) {
|
|
88630
|
+
writeMaterializedConstructionBuildCacheV1(
|
|
88631
|
+
workspace.root,
|
|
88632
|
+
workspace.state,
|
|
88633
|
+
localResult.materializedConstructors
|
|
88634
|
+
);
|
|
88635
|
+
}
|
|
88286
88636
|
}
|
|
88637
|
+
const pulledRecords = changedRecordKeys === null ? `Pulled ${document.records.size} records` : `Pulled ${changedRecordKeys.size} changed record(s)`;
|
|
88287
88638
|
const summary = [
|
|
88288
|
-
|
|
88639
|
+
pulledRecords,
|
|
88289
88640
|
`${written} file(s) updated`,
|
|
88290
88641
|
`${removed} removed`
|
|
88291
88642
|
];
|
|
@@ -88296,7 +88647,7 @@ async function finishFormat4Pull(args) {
|
|
|
88296
88647
|
if (mergedCount > 0)
|
|
88297
88648
|
summary.push(color.green(`${mergedCount} merged cleanly`));
|
|
88298
88649
|
if (conflictCount > 0) summary.push(color.red(`${conflictCount} CONFLICTED`));
|
|
88299
|
-
|
|
88650
|
+
progress.succeed(`${summary.join(" \u2014 ")}.`);
|
|
88300
88651
|
if (conflictCount > 0) {
|
|
88301
88652
|
warn(
|
|
88302
88653
|
'Conflict markers were written into the affected files. Edit them to the desired final state and push \u2014 the push is the resolution. To keep one side everywhere, use "neo resolve --mine" or "neo resolve --theirs".'
|
|
@@ -88310,6 +88661,98 @@ async function finishFormat4Pull(args) {
|
|
|
88310
88661
|
for (const path of binaries.conflicts) console.log(` ${sym.fail} ${path}`);
|
|
88311
88662
|
}
|
|
88312
88663
|
}
|
|
88664
|
+
function deltaRequiresSourceProjectionV4(args) {
|
|
88665
|
+
const mainLocale = workspaceMainLocale(args.workspace.state.records);
|
|
88666
|
+
for (const key of args.changedRecordKeys) {
|
|
88667
|
+
const previous = args.workspace.state.records[key];
|
|
88668
|
+
const local = args.localStatus.reconstructed.get(key)?.fullData;
|
|
88669
|
+
const plan = args.plans.get(key);
|
|
88670
|
+
const server = args.document.records.get(key);
|
|
88671
|
+
if (server === void 0) {
|
|
88672
|
+
if (previous?.file !== void 0 || local !== void 0) return true;
|
|
88673
|
+
continue;
|
|
88674
|
+
}
|
|
88675
|
+
if (local === void 0 || plan?.emitData === void 0) {
|
|
88676
|
+
if (previous?.file !== void 0 || previous === void 0) return true;
|
|
88677
|
+
continue;
|
|
88678
|
+
}
|
|
88679
|
+
if (!sourceAuthoredRecordsSemanticallyEqual(
|
|
88680
|
+
server.recordKind,
|
|
88681
|
+
plan.emitData,
|
|
88682
|
+
local,
|
|
88683
|
+
mainLocale
|
|
88684
|
+
)) {
|
|
88685
|
+
return true;
|
|
88686
|
+
}
|
|
88687
|
+
}
|
|
88688
|
+
return false;
|
|
88689
|
+
}
|
|
88690
|
+
function finishRecordOnlyDeltaPull(args) {
|
|
88691
|
+
const cachedExpressions = readMaterializedConstructionBuildCacheV1(
|
|
88692
|
+
args.workspace.root,
|
|
88693
|
+
args.workspace.state
|
|
88694
|
+
);
|
|
88695
|
+
const records2 = { ...args.workspace.state.records };
|
|
88696
|
+
for (const key of args.changedRecordKeys) {
|
|
88697
|
+
const server = args.document.records.get(key);
|
|
88698
|
+
if (server === void 0) {
|
|
88699
|
+
delete records2[key];
|
|
88700
|
+
continue;
|
|
88701
|
+
}
|
|
88702
|
+
const previous = records2[key];
|
|
88703
|
+
records2[key] = {
|
|
88704
|
+
recordKind: server.recordKind,
|
|
88705
|
+
recordId: server.recordId,
|
|
88706
|
+
contentHash: server.contentHash,
|
|
88707
|
+
data: server.data,
|
|
88708
|
+
...previous?.file === void 0 ? {} : { file: previous.file },
|
|
88709
|
+
...previous?.projectBinary === void 0 ? {} : { projectBinary: previous.projectBinary }
|
|
88710
|
+
};
|
|
88711
|
+
}
|
|
88712
|
+
args.workspace.state.records = records2;
|
|
88713
|
+
if (args.nextCursor !== null) {
|
|
88714
|
+
args.workspace.state.documentRevisionCursor = mutableCursor(
|
|
88715
|
+
args.nextCursor
|
|
88716
|
+
);
|
|
88717
|
+
}
|
|
88718
|
+
if (args.observedHeadTransactionHash !== void 0) {
|
|
88719
|
+
args.workspace.state.headTransactionHash = args.observedHeadTransactionHash;
|
|
88720
|
+
}
|
|
88721
|
+
writeWorkspaceState(args.workspace.root, args.workspace.state);
|
|
88722
|
+
if (cachedExpressions !== null) {
|
|
88723
|
+
writeMaterializedConstructionBuildCacheV1(
|
|
88724
|
+
args.workspace.root,
|
|
88725
|
+
args.workspace.state,
|
|
88726
|
+
cachedExpressions
|
|
88727
|
+
);
|
|
88728
|
+
}
|
|
88729
|
+
args.progress.succeed(
|
|
88730
|
+
`Pulled ${args.changedRecordKeys.size} changed record(s) \u2014 0 file(s) updated \u2014 0 removed.`
|
|
88731
|
+
);
|
|
88732
|
+
}
|
|
88733
|
+
function cursorFromRevision(revision) {
|
|
88734
|
+
return {
|
|
88735
|
+
createdAt: revision.latestTransactionCreatedAt,
|
|
88736
|
+
transactionIds: revision.latestTransactionId === null ? [] : [revision.latestTransactionId],
|
|
88737
|
+
versionsStamp: revision.versionsStamp
|
|
88738
|
+
};
|
|
88739
|
+
}
|
|
88740
|
+
function mutableCursor(cursor) {
|
|
88741
|
+
return {
|
|
88742
|
+
createdAt: cursor.createdAt,
|
|
88743
|
+
transactionIds: [...cursor.transactionIds],
|
|
88744
|
+
versionsStamp: cursor.versionsStamp
|
|
88745
|
+
};
|
|
88746
|
+
}
|
|
88747
|
+
function cursorsEqual(left, right) {
|
|
88748
|
+
if (left === void 0) return false;
|
|
88749
|
+
if (left.createdAt !== right.createdAt) return false;
|
|
88750
|
+
if (left.versionsStamp !== right.versionsStamp) return false;
|
|
88751
|
+
if (left.transactionIds.length !== right.transactionIds.length) return false;
|
|
88752
|
+
return left.transactionIds.every(
|
|
88753
|
+
(transactionId, index) => transactionId === right.transactionIds[index]
|
|
88754
|
+
);
|
|
88755
|
+
}
|
|
88313
88756
|
function projectSourcePathsRequiringRewriteV4(args) {
|
|
88314
88757
|
if (args.destructive || args.localStatus === null) {
|
|
88315
88758
|
return new Set(args.emittedPaths);
|
package/package.json
CHANGED
|
@@ -78,7 +78,7 @@ wrappers.
|
|
|
78
78
|
The marker near the top of `SKILL.md` must exactly match the package version:
|
|
79
79
|
|
|
80
80
|
```html
|
|
81
|
-
<!-- reviewed-through-cli: 0.22.
|
|
81
|
+
<!-- reviewed-through-cli: 0.22.2 -->
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
The quoted version above is checked too, so this instruction cannot go stale
|
|
@@ -28,7 +28,9 @@ neo push --dry-run --force-recompile
|
|
|
28
28
|
Use `neo pull --regenerate-source-names` to refresh readable naming while
|
|
29
29
|
retaining normal merge behavior. Use `--reset` to regenerate canonical source
|
|
30
30
|
and managed binaries. Use `--force` only to discard local edits for the server
|
|
31
|
-
version.
|
|
31
|
+
version. Normal pulls persist a transaction cursor and fetch only changed
|
|
32
|
+
records; an undo/reset metadata change invalidates the cursor and causes a safe
|
|
33
|
+
full-snapshot fallback.
|
|
32
34
|
|
|
33
35
|
Treat `neo push --dry-run` as a full local commit rehearsal: source emission,
|
|
34
36
|
in-memory pending-ID assignment, transport shapes, hashes, and repeat
|