@neocompose/cli 0.53.5 → 0.53.7

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,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.53.7] - 2026-09-22
4
+
5
+ - Adding a value under a row that stores packed children, such as a new list on an item whose sprite is packed, keeps those children readable in `neo test`, `neo status`, and `neo push --dry-run`. A constructed instance replayed from that row used to receive the child's id instead of its value and fail with "NSFunction argument 1 does not match its declared runtime type."
6
+ - Editing a function's or property's source drops its stale compiled body before local verification. Changing a function's parameters while keeping its `@id` no longer fails with 'Convex project document field "members" has an invalid shape'.
7
+
8
+ ## [0.53.6] - 2026-09-19
9
+
10
+ - Full project reads, including initial pulls and resyncs, fetch published snapshots directly in bounded pages, reducing Convex requests and duplicate database reads. Normal incremental pulls keep their existing delta transport.
11
+
3
12
  ## [0.53.5] - 2026-09-19
4
13
 
5
14
  - `neo test` and `neo push --dry-run` compile pending member initializers and their newly authored callable dependencies when a deletion replays a surviving constructed instance. Adding a computed default and deleting an owned row in one push no longer fails with "has no compiled body".
package/dist/neo.mjs CHANGED
@@ -38465,16 +38465,26 @@ function composeDocumentRecord(recordKind, recordId, authoredFields, baseRecord)
38465
38465
  }
38466
38466
  assertKnownDocumentFields(recordKind, baseRecord.data);
38467
38467
  const base = partitionDocumentFields(recordKind, baseRecord.data);
38468
- const invalidatesDerivedConstructor = recordKind === "constructor" && !schemaDocumentAuthoredSemanticallyEqual(
38468
+ const derived = Object.entries(base.derived);
38469
+ const authoredEdited = derived.length > 0 && !schemaDocumentAuthoredSemanticallyEqual(
38469
38470
  recordKind,
38470
38471
  baseRecord.data,
38471
38472
  normalizedAuthored
38472
38473
  );
38473
- if (!invalidatesDerivedConstructor) Object.assign(data, base.derived);
38474
+ for (const [field, value] of derived) {
38475
+ if (authoredEdited && compiledFromAuthoredBody(recordKind, field)) {
38476
+ continue;
38477
+ }
38478
+ data[field] = value;
38479
+ }
38474
38480
  Object.assign(data, base.volatile);
38475
38481
  }
38476
38482
  return { recordKind, recordId, data };
38477
38483
  }
38484
+ function compiledFromAuthoredBody(recordKind, field) {
38485
+ if (recordKind === "constructor") return true;
38486
+ return recordKind === "member" && field !== "valueId";
38487
+ }
38478
38488
  function assertOnlyAuthoredFields(recordKind, fields) {
38479
38489
  for (const field of Object.keys(fields)) {
38480
38490
  const disposition = fieldDisposition(recordKind, field);
@@ -86205,8 +86215,8 @@ __export(project_document_read_exports, {
86205
86215
  readArrayField: () => readArrayField,
86206
86216
  readProjectDocument: () => readProjectDocument,
86207
86217
  readProjectDocumentContentHashHeads: () => readProjectDocumentContentHashHeads,
86208
- readProjectDocumentManifestPage: () => readProjectDocumentManifestPage,
86209
86218
  readProjectDocumentManifestPageRecords: () => readProjectDocumentManifestPageRecords,
86219
+ readProjectDocumentPage: () => readProjectDocumentPage,
86210
86220
  readProjectDocumentRevisionMarker: () => readProjectDocumentRevisionMarker,
86211
86221
  withStoredValueBase: () => withStoredValueBase
86212
86222
  });
@@ -86224,7 +86234,7 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86224
86234
  "Chunked project document read received an empty partition key."
86225
86235
  );
86226
86236
  }
86227
- const firstPageResult = await runner.fetchManifestPage(
86237
+ const firstPageResult = await runner.fetchPage(
86228
86238
  {
86229
86239
  numItems: DOCUMENT_MANIFEST_PAGE_SIZE,
86230
86240
  cursor: null
@@ -86232,7 +86242,7 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86232
86242
  firstMapKey
86233
86243
  );
86234
86244
  if (firstPageResult === null) return null;
86235
- const firstPage = readProjectDocumentManifestPage(firstPageResult);
86245
+ const firstPage = readProjectDocumentPage(firstPageResult);
86236
86246
  if (firstPage.metadata === null) {
86237
86247
  throw new Error(
86238
86248
  "Convex project document manifest first page is missing version metadata."
@@ -86244,18 +86254,20 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86244
86254
  const snapshots = createProjectDocumentSnapshotLoader(
86245
86255
  (ids) => fetchScopedSnapshotBatch(runner, ids, readBase)
86246
86256
  );
86247
- const rememberRecords = async (records2) => {
86257
+ const rememberRecords = async (page) => {
86258
+ if (page.snapshots !== void 0) snapshots.seed(page.snapshots);
86259
+ const records2 = page.records;
86248
86260
  for (const record6 of records2) {
86249
86261
  recordsByKey.set(`${record6.recordKind} ${record6.recordId}`, record6);
86250
86262
  }
86251
86263
  await snapshots.enqueue(records2.map((record6) => record6.snapshotId));
86252
86264
  };
86253
86265
  try {
86254
- await rememberRecords(firstPage.records);
86266
+ await rememberRecords(firstPage);
86255
86267
  const fetchRemainingPages = async (mapKey, initialCursor) => {
86256
86268
  let cursor = initialCursor;
86257
86269
  while (cursor !== null) {
86258
- const pageResult = await runner.fetchManifestPage(
86270
+ const pageResult = await runner.fetchPage(
86259
86271
  {
86260
86272
  numItems: DOCUMENT_MANIFEST_PAGE_SIZE,
86261
86273
  cursor
@@ -86268,15 +86280,15 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86268
86280
  "Convex project document manifest returned null for a continuation page."
86269
86281
  );
86270
86282
  }
86271
- const page = readProjectDocumentManifestPage(pageResult);
86283
+ const page = readProjectDocumentPage(pageResult);
86272
86284
  assertProjectReadBase(readBase, page.readBase);
86273
- await rememberRecords(page.records);
86285
+ await rememberRecords(page);
86274
86286
  cursor = page.continueCursor;
86275
86287
  }
86276
86288
  };
86277
86289
  await fetchRemainingPages(firstMapKey, firstPage.continueCursor);
86278
86290
  for (const mapKey of mapKeys.slice(1)) {
86279
- const pageResult = await runner.fetchManifestPage(
86291
+ const pageResult = await runner.fetchPage(
86280
86292
  { numItems: DOCUMENT_MANIFEST_PAGE_SIZE, cursor: null },
86281
86293
  mapKey,
86282
86294
  readBase,
@@ -86287,9 +86299,9 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86287
86299
  `Convex project document manifest returned null for partition "${mapKey ?? "all"}".`
86288
86300
  );
86289
86301
  }
86290
- const page = readProjectDocumentManifestPage(pageResult);
86302
+ const page = readProjectDocumentPage(pageResult);
86291
86303
  assertProjectReadBase(readBase, page.readBase);
86292
- await rememberRecords(page.records);
86304
+ await rememberRecords(page);
86293
86305
  await fetchRemainingPages(mapKey, page.continueCursor);
86294
86306
  }
86295
86307
  const snapshotsById = await snapshots.finish();
@@ -86320,7 +86332,8 @@ async function fetchProjectDocumentRawChunkedOnce(runner, options = {}) {
86320
86332
  }
86321
86333
  }
86322
86334
  const records2 = [...recordsByKey.values()];
86323
- await runner.validateReadBase(readBase);
86335
+ if (options.revisionValidation !== "live-subscription")
86336
+ await runner.validateReadBase(readBase);
86324
86337
  return assembleProjectDocumentRaw({
86325
86338
  version: metadata.version,
86326
86339
  versions: metadata.versions,
@@ -86352,7 +86365,7 @@ async function fetchProjectDocumentPartitionRecords(runner, mapKey, readBase) {
86352
86365
  try {
86353
86366
  let cursor = null;
86354
86367
  do {
86355
- const pageResult = await runner.fetchManifestPage(
86368
+ const pageResult = await runner.fetchPage(
86356
86369
  { numItems: DOCUMENT_MANIFEST_PAGE_SIZE, cursor },
86357
86370
  mapKey,
86358
86371
  readBase,
@@ -86363,11 +86376,12 @@ async function fetchProjectDocumentPartitionRecords(runner, mapKey, readBase) {
86363
86376
  `Convex project document manifest returned null for partition "${mapKey}".`
86364
86377
  );
86365
86378
  }
86366
- const page = readProjectDocumentManifestPage(pageResult);
86379
+ const page = readProjectDocumentPage(pageResult);
86367
86380
  assertProjectReadBase(readBase, page.readBase);
86368
86381
  for (const record6 of page.records) {
86369
86382
  recordsByKey.set(`${record6.recordKind} ${record6.recordId}`, record6);
86370
86383
  }
86384
+ if (page.snapshots !== void 0) snapshots.seed(page.snapshots);
86371
86385
  await snapshots.enqueue(page.records.map((record6) => record6.snapshotId));
86372
86386
  cursor = page.continueCursor;
86373
86387
  } while (cursor !== null);
@@ -86393,42 +86407,30 @@ async function fetchProjectDocumentPartitionRecords(runner, mapKey, readBase) {
86393
86407
  }
86394
86408
  }
86395
86409
  async function fetchProjectDocumentValueRecords(runner, valueIds, readBase) {
86396
- if (runner.fetchValueHeadsPage === void 0) {
86410
+ if (runner.fetchValueSnapshotsPage === void 0) {
86397
86411
  throw new Error(
86398
- "Partition-scoped document reads require a value-head reader for member defaults."
86412
+ "Partition-scoped document reads require a value-snapshot reader for member defaults."
86399
86413
  );
86400
86414
  }
86401
86415
  const values = /* @__PURE__ */ new Map();
86402
86416
  let cursor = null;
86403
86417
  do {
86404
- const page = await runner.fetchValueHeadsPage(valueIds, cursor, readBase);
86405
- if (!isObject4(page)) throw new Error("Value-head page must be an object.");
86406
- const revision = readProjectDocumentRevisionMarker({
86407
- documentRevision: page.documentRevision
86408
- });
86409
- if (revision === null)
86410
- throw new Error("Value-head page has no document revision.");
86411
- assertProjectReadBase(readBase, revision.readBase);
86412
- if (!Array.isArray(page.snapshotIds) || !page.snapshotIds.every((id2) => typeof id2 === "string"))
86413
- throw new Error(
86414
- "Value-head page snapshotIds must be an array of strings."
86415
- );
86416
- if (page.continueCursor !== null && typeof page.continueCursor !== "string")
86417
- throw new Error(
86418
- "Value-head page continueCursor must be a string or null."
86419
- );
86420
- const loaded = await fetchProjectDocumentSnapshots(
86421
- (ids) => fetchScopedSnapshotBatch(runner, ids, readBase),
86422
- page.snapshotIds
86423
- );
86424
- for (const snapshot of loaded.values()) {
86418
+ const page = readProjectDocumentPage(
86419
+ await runner.fetchValueSnapshotsPage(valueIds, cursor, readBase)
86420
+ );
86421
+ assertProjectReadBase(readBase, page.readBase);
86422
+ if (page.snapshots === void 0)
86423
+ throw new Error("Value snapshot page is missing snapshots.");
86424
+ if (page.continueCursor !== null && page.continueCursor === cursor)
86425
+ throw new Error("Value snapshot page cursor made no progress.");
86426
+ for (const snapshot of page.snapshots) {
86425
86427
  if (snapshot.recordKind !== "value")
86426
- throw new Error("Value-head page returned a non-value snapshot.");
86428
+ throw new Error("Value snapshot page returned a non-value snapshot.");
86427
86429
  if (snapshot.deleted)
86428
- throw new Error("Value-head page returned a deleted snapshot.");
86430
+ throw new Error("Value snapshot page returned a deleted snapshot.");
86429
86431
  if (!valueIds.includes(snapshot.recordId))
86430
86432
  throw new Error(
86431
- `Value-head page returned unrequested value "${snapshot.recordId}".`
86433
+ `Value snapshot page returned unrequested value "${snapshot.recordId}".`
86432
86434
  );
86433
86435
  values.set(snapshot.recordId, snapshot);
86434
86436
  }
@@ -86446,10 +86448,23 @@ async function fetchScopedSnapshotBatch(runner, ids, readBase) {
86446
86448
  assertProjectReadBase(readBase, readProjectReadBase(result.readBase));
86447
86449
  return result;
86448
86450
  }
86449
- function readProjectDocumentManifestPage(value) {
86451
+ function readProjectDocumentPage(value) {
86450
86452
  if (!isObject4(value)) {
86451
86453
  throw new Error("Convex project document manifest page must be an object.");
86452
86454
  }
86455
+ let snapshots;
86456
+ if (value.snapshots !== void 0) {
86457
+ if (!Array.isArray(value.snapshots))
86458
+ throw new Error("Snapshot page snapshots must be an array.");
86459
+ const ids = value.snapshots.map((snapshot) => {
86460
+ if (!isObject4(snapshot))
86461
+ throw new Error("Snapshot page entry must be an object.");
86462
+ if (typeof snapshot.id !== "string")
86463
+ throw new Error("Snapshot page entry is missing an id.");
86464
+ return snapshot.id;
86465
+ });
86466
+ snapshots = ids.length === 0 ? [] : readProjectDocumentSnapshotBatch(value, ids);
86467
+ }
86453
86468
  const continueCursor = value.continueCursor;
86454
86469
  if (continueCursor !== null && typeof continueCursor !== "string") {
86455
86470
  throw new Error(
@@ -86472,7 +86487,12 @@ function readProjectDocumentManifestPage(value) {
86472
86487
  releaseChannels: metadata.releaseChannels,
86473
86488
  documentRevision: metadata.documentRevision
86474
86489
  },
86475
- records: readProjectDocumentManifestRecords(value.records),
86490
+ ...snapshots === void 0 ? {} : { snapshots },
86491
+ records: snapshots === void 0 ? readProjectDocumentManifestRecords(value.records) : snapshots.map((snapshot) => ({
86492
+ recordKind: snapshot.recordKind,
86493
+ recordId: snapshot.recordId,
86494
+ snapshotId: snapshot.id
86495
+ })),
86476
86496
  continueCursor
86477
86497
  };
86478
86498
  }
@@ -86520,7 +86540,7 @@ function readProjectDocumentManifestPageRecords(value) {
86520
86540
  if (!isObject4(value)) {
86521
86541
  throw new Error("Convex project document manifest page must be an object.");
86522
86542
  }
86523
- return readProjectDocumentManifestRecords(value.records);
86543
+ return readProjectDocumentPage(value).records;
86524
86544
  }
86525
86545
  async function fetchProjectDocumentSnapshots(fetchBatch, snapshotIds) {
86526
86546
  const loader = createProjectDocumentSnapshotLoader(fetchBatch);
@@ -86566,6 +86586,12 @@ function createProjectDocumentSnapshotLoader(fetchBatch) {
86566
86586
  active.add(work);
86567
86587
  };
86568
86588
  return {
86589
+ seed(snapshots) {
86590
+ for (const snapshot of snapshots) {
86591
+ seen.add(snapshot.id);
86592
+ snapshotsById.set(snapshot.id, snapshot);
86593
+ }
86594
+ },
86569
86595
  async enqueue(snapshotIds) {
86570
86596
  checkFailure();
86571
86597
  for (const id2 of snapshotIds) {
@@ -116045,36 +116071,59 @@ function prospectiveInitializerRecordsV4(args) {
116045
116071
  }
116046
116072
  for (const seed of args.authoredValueSeeds) {
116047
116073
  if (seed.valueId === void 0) continue;
116048
- records2.set(`value:${seed.valueId}`, {
116049
- recordKind: "value",
116050
- recordId: seed.valueId,
116051
- contentHash: "local-seed",
116052
- deleted: false,
116053
- data: {
116054
- id: seed.valueId,
116074
+ setSeedValueRecord(records2, {
116075
+ id: seed.valueId,
116076
+ projectId: args.workspace.config.projectId,
116077
+ ...encodeStoredValueNode(seed),
116078
+ createdAt: 0,
116079
+ updatedAt: 0
116080
+ });
116081
+ for (const row of seed.values ?? []) {
116082
+ setSeedValueRecord(records2, {
116083
+ id: row.id,
116055
116084
  projectId: args.workspace.config.projectId,
116056
- ...encodeStoredValueNode(seed),
116085
+ ...encodeStoredValueRowFields(row),
116057
116086
  createdAt: 0,
116058
116087
  updatedAt: 0
116059
- }
116060
- });
116061
- for (const row of seed.values ?? []) {
116062
- records2.set(`value:${row.id}`, {
116088
+ });
116089
+ }
116090
+ }
116091
+ return records2;
116092
+ }
116093
+ function setSeedValueRecord(records2, data) {
116094
+ const key = `value:${data.id}`;
116095
+ const physical = records2.get(key)?.data;
116096
+ if (isObjectRecord2(physical) && rowCarriesPackedValueContent(physical)) {
116097
+ const row = {
116098
+ projectId: data.projectId,
116099
+ createdAt: 0,
116100
+ updatedAt: 0,
116101
+ ...physical
116102
+ };
116103
+ if (!isMemberValue(row)) {
116104
+ throw new PackedValueEncodingError(
116105
+ `Seeded row "${key}" replaces a row that packs a child but is not a well-formed value row, so its packed children cannot be kept.`
116106
+ );
116107
+ }
116108
+ for (const child of decodePackedValueRow(row).children) {
116109
+ const childKey = `value:${child.id}`;
116110
+ if (records2.has(childKey)) continue;
116111
+ records2.set(childKey, {
116063
116112
  recordKind: "value",
116064
- recordId: row.id,
116113
+ recordId: child.id,
116065
116114
  contentHash: "local-seed",
116066
116115
  deleted: false,
116067
- data: {
116068
- id: row.id,
116069
- projectId: args.workspace.config.projectId,
116070
- ...encodeStoredValueRowFields(row),
116071
- createdAt: 0,
116072
- updatedAt: 0
116073
- }
116116
+ data: child
116074
116117
  });
116075
116118
  }
116076
116119
  }
116077
- return records2;
116120
+ records2.set(key, {
116121
+ recordKind: "value",
116122
+ recordId: data.id,
116123
+ contentHash: "local-seed",
116124
+ deleted: false,
116125
+ data
116126
+ });
116078
116127
  }
116079
116128
  function initializerCode(records2, valueId) {
116080
116129
  const row = records2.get(`value:${valueId}`)?.data;
@@ -133618,7 +133667,7 @@ function readRawSingleton(raw, key) {
133618
133667
  async function fetchProjectDocument(workspace) {
133619
133668
  const convex = await createConvexClient(workspace);
133620
133669
  const raw = await fetchProjectDocumentRawChunked2({
133621
- fetchManifestPage: (paginationOpts, mapKey, readBase) => convex.query(api2.projectDocuments.getDocumentManifestPage, {
133670
+ fetchPage: (paginationOpts, mapKey, readBase) => convex.query(api2.projectDocuments.getDocumentSnapshotPage, {
133622
133671
  projectId: workspace.config.projectId,
133623
133672
  versionId: workspace.config.versionId,
133624
133673
  paginationOpts,
@@ -138787,7 +138836,7 @@ var init_registry2 = __esm({
138787
138836
  "schema-contract/registry.mjs"() {
138788
138837
  "use strict";
138789
138838
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
138790
- cliVersion: "0.53.5",
138839
+ cliVersion: "0.53.7",
138791
138840
  projectFileUploadBatchSize: 32,
138792
138841
  documentRecords: {
138793
138842
  member: {
@@ -146608,7 +146657,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
146608
146657
  async function main() {
146609
146658
  const args = parseArgs(process.argv.slice(2));
146610
146659
  if (args.command === "--version") {
146611
- console.log("0.53.5");
146660
+ console.log("0.53.7");
146612
146661
  return;
146613
146662
  }
146614
146663
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.53.5",
3
+ "version": "0.53.7",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -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.53.5 -->
12
+ <!-- reviewed-through-cli: 0.53.7 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -97,7 +97,7 @@ wrappers.
97
97
  The marker near the top of `SKILL.md` must exactly match the package version:
98
98
 
99
99
  ```html
100
- <!-- reviewed-through-cli: 0.53.5 -->
100
+ <!-- reviewed-through-cli: 0.53.7 -->
101
101
  ```
102
102
 
103
103
  The quoted version above is checked too, so this instruction cannot go stale