@neocompose/cli 0.10.1 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.3] - 2026-07-25
4
+
5
+ ### Fixed
6
+
7
+ - Stop writing `isReadOnly: false` onto members that never carried the field.
8
+ Absent is how false is spelled, but document normalization materialized the
9
+ full-member default, so any member record that changed for another reason
10
+ pushed an explicit `isReadOnly: false` with it. The explicit value is kept
11
+ only where it has something to overwrite -- a record that already carries
12
+ the field, which is how dropping `readonly` from a declaration reaches the
13
+ server.
14
+
15
+ ## [0.10.2] - 2026-07-25
16
+
17
+ ### Fixed
18
+
19
+ - Read `required` and an override's `extendsMemberId` from source instead of
20
+ carrying the pulled value forward. A member whose stored `required`
21
+ contradicted its own return type, and an override pointing past the member
22
+ it directly overrides, are corrected on the next push.
23
+ - Persist a class's `schemaKeyOrder` only where the declaration order differs
24
+ from the one its schema map already implies. An equivalent order is stored
25
+ as absent rather than spelled out, and the answer no longer depends on
26
+ having pulled the class.
27
+ - Restore the server-owned envelope on reconstructed records before
28
+ prospective animation validation, so a workspace with animation schema no
29
+ longer reports every class as malformed. Validation failures now name the
30
+ offending record.
31
+
3
32
  ## [0.10.1] - 2026-07-25
4
33
 
5
34
  ### Changed
package/dist/neo.mjs CHANGED
@@ -1356,6 +1356,9 @@ function composeDocumentRecord(recordKind, recordId, authoredFields, baseRecord)
1356
1356
  const base = partitionDocumentFields(recordKind, baseRecord.data);
1357
1357
  Object.assign(data, base.derived, base.volatile);
1358
1358
  }
1359
+ if (recordKind === "member" && data.isReadOnly === false && !Object.prototype.hasOwnProperty.call(baseRecord?.data ?? {}, "isReadOnly")) {
1360
+ delete data.isReadOnly;
1361
+ }
1359
1362
  return { recordKind, recordId, data };
1360
1363
  }
1361
1364
  function assertOnlyAuthoredFields(recordKind, fields) {
@@ -28915,11 +28918,7 @@ function lowerClass(context, declaration) {
28915
28918
  selectionSpan: span(parameter3.identity)
28916
28919
  };
28917
28920
  });
28918
- const baseEffectiveOrder = base?.schemaKeyOrder ?? (base ? Object.keys(base.schema) : []);
28919
- const baseOwnOrder = baseEffectiveOrder.filter(
28920
- (name) => Object.hasOwn(base?.schema ?? {}, name)
28921
- );
28922
- const persistedSchemaOrder = base && JSON.stringify(baseOwnOrder) === JSON.stringify(schemaKeyOrder) ? base.schemaKeyOrder : schemaKeyOrder;
28921
+ const persistedSchemaOrder = isImpliedSchemaKeyOrder(schemaKeyOrder) ? null : schemaKeyOrder;
28923
28922
  return {
28924
28923
  id: id2,
28925
28924
  source: sourceIdentity2(declaration, "class", declaration.name),
@@ -29120,7 +29119,7 @@ function projectSchemaJsonValue(value) {
29120
29119
  }
29121
29120
  function memberCommon(context, ownerClass, declaration, id2, owner, base) {
29122
29121
  const storage = annotation(declaration.annotations, "storage");
29123
- const inheritedId = declaration.modifiers.includes("override") || declaration.modifiers.includes("sealed") ? base?.overrideOf ?? inheritedMemberId(context, ownerClass, declaration.name) : null;
29122
+ const inheritedId = declaration.modifiers.includes("override") || declaration.modifiers.includes("sealed") ? inheritedMemberId(context, ownerClass, declaration.name) : null;
29124
29123
  return {
29125
29124
  id: id2,
29126
29125
  source: sourceIdentity2(declaration, "member", declaration.name),
@@ -29135,7 +29134,10 @@ function memberCommon(context, ownerClass, declaration, id2, owner, base) {
29135
29134
  accessModifier: owner.kind === "classMember" ? effectiveSourceAccessModifier(declaration.modifiers, "class") : "public",
29136
29135
  modifier: memberModifier2(declaration.modifiers, base?.modifier),
29137
29136
  locked: hasAnnotation(declaration.annotations, "locked"),
29138
- required: declaration.kind === "field" ? !declaration.type.nullable : base?.required ?? false,
29137
+ // Every declaration kind carries a type, and its nullability is the whole
29138
+ // of what `required` means. Properties and getters used to fall back to
29139
+ // the pulled value, which left a fresh workspace calling them optional.
29140
+ required: !declaration.type.nullable,
29139
29141
  defaultValue: null,
29140
29142
  overrideOf: inheritedId,
29141
29143
  system: systemMetadata(declaration.annotations),
@@ -30161,6 +30163,9 @@ function referenceId(raw) {
30161
30163
  return null;
30162
30164
  }
30163
30165
  }
30166
+ function isImpliedSchemaKeyOrder(order) {
30167
+ return order.every((key, index) => index === 0 || order[index - 1] <= key);
30168
+ }
30164
30169
  function memberModifier2(modifiers, baseModifier) {
30165
30170
  if (modifiers.includes("override")) {
30166
30171
  if (modifiers.includes("abstract")) return "abstractOverride";
@@ -48915,28 +48920,28 @@ function validateProspectiveAnimationRecordsV4(records2) {
48915
48920
  if (record3.recordKind === "project") {
48916
48921
  if (!isProject(record3.data)) {
48917
48922
  throw new Error(
48918
- "Prospective animation validation received an invalid project record."
48923
+ `Prospective animation validation received an invalid project record ${recordLabel(record3.data)}.`
48919
48924
  );
48920
48925
  }
48921
48926
  project = record3.data;
48922
48927
  } else if (record3.recordKind === "class") {
48923
48928
  if (!isNeoSchemaClass(record3.data)) {
48924
48929
  throw new Error(
48925
- "Prospective animation validation received an invalid class record."
48930
+ `Prospective animation validation received an invalid class record ${recordLabel(record3.data)}.`
48926
48931
  );
48927
48932
  }
48928
48933
  classes.push(record3.data);
48929
48934
  } else if (record3.recordKind === "member") {
48930
48935
  if (!isMember(record3.data)) {
48931
48936
  throw new Error(
48932
- "Prospective animation validation received an invalid member record."
48937
+ `Prospective animation validation received an invalid member record ${recordLabel(record3.data)}.`
48933
48938
  );
48934
48939
  }
48935
48940
  members.push(record3.data);
48936
48941
  } else if (record3.recordKind === "value") {
48937
48942
  if (!isMemberValue(record3.data)) {
48938
48943
  throw new Error(
48939
- "Prospective animation validation received an invalid value record."
48944
+ `Prospective animation validation received an invalid value record ${recordLabel(record3.data)}.`
48940
48945
  );
48941
48946
  }
48942
48947
  values.push(record3.data);
@@ -48945,6 +48950,18 @@ function validateProspectiveAnimationRecordsV4(records2) {
48945
48950
  if (project === void 0) return;
48946
48951
  assertAnimationClipDocumentValid({ project, classes, members, values });
48947
48952
  }
48953
+ function serverEnvelope(stored) {
48954
+ if (!isObjectRecord2(stored)) return {};
48955
+ const envelope = {};
48956
+ for (const field of ["projectId", "createdAt", "updatedAt"]) {
48957
+ if (stored[field] !== void 0) envelope[field] = stored[field];
48958
+ }
48959
+ return envelope;
48960
+ }
48961
+ function recordLabel(data) {
48962
+ const id2 = typeof data.id === "string" ? data.id : "<no id>";
48963
+ return typeof data.name === "string" ? `${id2} ("${data.name}")` : id2;
48964
+ }
48948
48965
  function prospectiveAnimationRecords(base, reconstructed3, changes, seeds) {
48949
48966
  const prospective = /* @__PURE__ */ new Map();
48950
48967
  for (const [key, state] of Object.entries(base)) {
@@ -48957,16 +48974,25 @@ function prospectiveAnimationRecords(base, reconstructed3, changes, seeds) {
48957
48974
  const key = recordStateKey(change.recordKind, change.recordId);
48958
48975
  if (change.kind === "delete") prospective.delete(key);
48959
48976
  }
48960
- for (const [key, record3] of reconstructed3) {
48961
- prospective.set(key, {
48962
- recordKind: record3.recordKind,
48963
- data: record3.fullData
48964
- });
48965
- }
48966
- const projectId = [...prospective.values()].find(
48977
+ const projectRecord = [...prospective.values()].find(
48967
48978
  (record3) => record3.recordKind === "project"
48968
- )?.data.id;
48979
+ ) ?? [...reconstructed3.values()].filter((record3) => record3.recordKind === "project").map((record3) => ({
48980
+ recordKind: record3.recordKind,
48981
+ data: record3.fullData
48982
+ }))[0];
48983
+ const projectId = projectRecord?.data.id;
48969
48984
  const seedEnvelope = typeof projectId === "string" ? { projectId, createdAt: 0, updatedAt: 0 } : {};
48985
+ for (const [key, record3] of reconstructed3) {
48986
+ const envelope = {
48987
+ ...seedEnvelope,
48988
+ ...serverEnvelope(prospective.get(key)?.data)
48989
+ };
48990
+ const data = { ...record3.fullData };
48991
+ for (const [field, value] of Object.entries(envelope)) {
48992
+ if (data[field] === void 0) data[field] = value;
48993
+ }
48994
+ prospective.set(key, { recordKind: record3.recordKind, data });
48995
+ }
48970
48996
  for (const [memberId, seed] of seeds) {
48971
48997
  for (const bindingMember of seed.bindingMembers ?? []) {
48972
48998
  prospective.set(recordStateKey("member", bindingMember.id), {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.10.1",
3
+ "version": "0.10.3",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",