@neocompose/cli 0.10.3 → 0.10.5

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/dist/neo.mjs +136 -16
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.10.5] - 2026-07-26
4
+
5
+ ### Changed
6
+
7
+ - Stop writing a second `@id` above every static and project-root initializer.
8
+ A member owns exactly one value row, so that row's identity is a function of
9
+ the declaration the annotation sat under and never needed to be authored.
10
+ `neo pull` now emits the binding without it, and lowering resolves the row
11
+ itself. An `@id` left in a file is accepted while it agrees with the row the
12
+ member already owns, so existing workspaces keep working until their next
13
+ pull; one that disagrees is rejected rather than pushing a create for a
14
+ second row.
15
+ - Derive the row id for a binding that does not have one yet, instead of
16
+ minting it. A member declared for the first time has no durable id to derive
17
+ from, so it carries a `__pending__:member-value:` token that resolves to the
18
+ derivation once the push assigns the member; the server re-derives it rather
19
+ than accepting the client's answer. Rows that already exist keep the ids
20
+ they were minted with -- P39 moves those onto the derivation.
21
+
22
+ ## [0.10.4] - 2026-07-26
23
+
24
+ ### Fixed
25
+
26
+ - Materialize the required keys a bare `new()` omits. A constructor body was
27
+ built from constructor projections and explicit assignments only, so
28
+ `Birthday = new()` lowered to `{}` and the commit was rejected with
29
+ `missing required schema key "Day"` -- naming a key whose default the source
30
+ declares one line away. Each omitted required key now lowers the member's own
31
+ declared initializer, so a nested `new()` recurses and a row-backed default
32
+ is copied rather than shared. Optional keys stay absent, and a required key
33
+ whose member declares no default is unchanged.
34
+
35
+ - Stop rejecting the transaction status response over a field the server no
36
+ longer sends. `resultCursor` was removed from the status payload as dead --
37
+ it was hardcoded null and read only as the starting result-page cursor,
38
+ where an empty cursor means the same thing -- but push still required it to
39
+ be present, so every push large enough to go durable failed with
40
+ `resultCursor must be a string or null` after the write had already
41
+ committed.
42
+
3
43
  ## [0.10.3] - 2026-07-25
4
44
 
5
45
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -42179,6 +42179,27 @@ var init_dialogue_sources = __esm({
42179
42179
  }
42180
42180
  });
42181
42181
 
42182
+ // ../src/models/members/member-value-id.ts
42183
+ function derivedMemberValueId(memberId) {
42184
+ return v5_default(memberId, NEO_MEMBER_VALUE_NAMESPACE);
42185
+ }
42186
+ function pendingMemberValueId(pendingMemberId) {
42187
+ return `${PENDING_MEMBER_VALUE_PREFIX}${encodeURIComponent(pendingMemberId)}`;
42188
+ }
42189
+ function pendingMemberValueMemberId(token) {
42190
+ if (!token.startsWith(PENDING_MEMBER_VALUE_PREFIX)) return null;
42191
+ return decodeURIComponent(token.slice(PENDING_MEMBER_VALUE_PREFIX.length));
42192
+ }
42193
+ var NEO_MEMBER_VALUE_NAMESPACE, PENDING_MEMBER_VALUE_PREFIX;
42194
+ var init_member_value_id = __esm({
42195
+ "../src/models/members/member-value-id.ts"() {
42196
+ "use strict";
42197
+ init_dist_node();
42198
+ NEO_MEMBER_VALUE_NAMESPACE = "7f2b9c14-58d6-5a07-b3e1-4c9d0a6f8b25";
42199
+ PENDING_MEMBER_VALUE_PREFIX = "__pending__:member-value:";
42200
+ }
42201
+ });
42202
+
42182
42203
  // src/project-source/value-sources.ts
42183
42204
  function inferredGenericClassBinding(classId) {
42184
42205
  return `${INFERRED_GENERIC_CLASS_PREFIX}${classId}`;
@@ -42240,7 +42261,10 @@ function emitStoredValueBindingSourcesV4(records2, memberIds, options) {
42240
42261
  initializers.set(
42241
42262
  memberId,
42242
42263
  emitValue(context, member, member.valueId, {
42243
- exposeIdentity: true,
42264
+ definitionSite: true,
42265
+ // The row is `derivedMemberValueId(memberId)` and the declaration
42266
+ // above it already carries `@id(memberId)`.
42267
+ writeIdentity: false,
42244
42268
  targetTyped: options.targetTypedRoot === true,
42245
42269
  visited
42246
42270
  })
@@ -42417,6 +42441,7 @@ function buildValueLowerContext(state, manifest, options = {}) {
42417
42441
  mainLocale: projectMainLocaleFromState(state),
42418
42442
  valuePlacements: indexValuePlacements(state),
42419
42443
  parsedInitializers,
42444
+ declaredInitializers: indexDeclaredInitializers(options.analysis),
42420
42445
  reconstructed: /* @__PURE__ */ new Map(),
42421
42446
  pendingValues: /* @__PURE__ */ new Map(),
42422
42447
  pendingLocalizedTexts: /* @__PURE__ */ new Map(),
@@ -42424,6 +42449,27 @@ function buildValueLowerContext(state, manifest, options = {}) {
42424
42449
  pendingBindingMembersByClassId: /* @__PURE__ */ new Map()
42425
42450
  };
42426
42451
  }
42452
+ function indexDeclaredInitializers(analysis) {
42453
+ const initializers = /* @__PURE__ */ new Map();
42454
+ if (analysis === void 0) return initializers;
42455
+ for (const sourceClass of analysis.schema.classes) {
42456
+ for (const declaration of sourceClass.members) {
42457
+ if (declaration.kind !== "field" || declaration.initializer === null) {
42458
+ continue;
42459
+ }
42460
+ if (declaration.modifiers.includes("static")) continue;
42461
+ initializers.set(
42462
+ sourceIdentityId(
42463
+ declaration,
42464
+ "member",
42465
+ `${sourceClass.name}.${declaration.name}`
42466
+ ),
42467
+ declaration.initializer
42468
+ );
42469
+ }
42470
+ }
42471
+ return initializers;
42472
+ }
42427
42473
  function lowerStoredValueBindingsV4(state, manifest, bindings, options = {}) {
42428
42474
  const context = buildValueLowerContext(state, manifest, options);
42429
42475
  const memberValueIds = /* @__PURE__ */ new Map();
@@ -42667,8 +42713,8 @@ function lowerStoredBinding(context, binding, memberValueIds, seeds) {
42667
42713
  preserveReboundValue(context, currentValueId, referenced, binding);
42668
42714
  return;
42669
42715
  }
42670
- const annotatedId = annotatedValue(expression).id;
42671
- const valueId = annotatedId ?? currentValueId ?? pendingValueId(binding, binding.label);
42716
+ const valueId = currentValueId ?? memberValueId(binding.memberId);
42717
+ assertAuthoredBindingIdMatches(expression, binding, valueId);
42672
42718
  memberValueIds.set(binding.memberId, valueId);
42673
42719
  if (currentValueId === valueId) {
42674
42720
  const existingReconstructedKeys = new Set(context.reconstructed.keys());
@@ -42716,6 +42762,16 @@ function lowerStoredBinding(context, binding, memberValueIds, seeds) {
42716
42762
  const seed = lowerStaticSeed(context, member, expression, binding, valueId);
42717
42763
  seeds.set(binding.memberId, { ...seed, valueId });
42718
42764
  }
42765
+ function assertAuthoredBindingIdMatches(expression, binding, valueId) {
42766
+ const authoredId = annotatedValue(expression).id;
42767
+ if (authoredId === null || authoredId === valueId) return;
42768
+ throw new Error(
42769
+ `Binding ${binding.label} is annotated with value id ${authoredId}, but the member owns value ${valueId}. Remove the @id \u2014 \`neo pull\` rewrites this file without it.`
42770
+ );
42771
+ }
42772
+ function memberValueId(memberId) {
42773
+ return isPendingId(memberId) ? pendingMemberValueId(memberId) : derivedMemberValueId(memberId);
42774
+ }
42719
42775
  function reconstructedOrStateValue(context, valueId) {
42720
42776
  return context.reconstructed.get(`value:${valueId}`)?.fileFields ?? valueBase(context, valueId);
42721
42777
  }
@@ -42889,6 +42945,17 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
42889
42945
  environment
42890
42946
  );
42891
42947
  }
42948
+ materializeRequiredDefaults(
42949
+ context,
42950
+ resolvedMember,
42951
+ effectiveClass,
42952
+ body,
42953
+ source,
42954
+ path,
42955
+ rows,
42956
+ localizedTexts,
42957
+ environment
42958
+ );
42892
42959
  value = body;
42893
42960
  } else if (resolvedMember.kind === "list") {
42894
42961
  if (expression.kind === "litNull") {
@@ -42984,6 +43051,51 @@ function lowerSeedRow(context, member, sourceExpression, source, valueId, path,
42984
43051
  context.pendingValues.set(valueId, row);
42985
43052
  return row;
42986
43053
  }
43054
+ function materializeRequiredDefaults(context, member, effectiveClass, body, source, path, rows, localizedTexts, environment) {
43055
+ if (member.partial === true) return;
43056
+ for (const [schemaKey, childMember] of storedSchemaEntries(
43057
+ context,
43058
+ effectiveClass
43059
+ )) {
43060
+ if (body[schemaKey] !== void 0) continue;
43061
+ if (!childMember.required) continue;
43062
+ if (childMember.kind === "class" && childMember.partial === true) continue;
43063
+ const initializer = context.declaredInitializers.get(childMember.id);
43064
+ if (initializer === void 0) continue;
43065
+ body[schemaKey] = lowerSeedChild(
43066
+ context,
43067
+ recursivePartialMember(member, childMember),
43068
+ parseCachedInitializer(context.parsedInitializers, initializer),
43069
+ source,
43070
+ `${path}.${schemaKey}`,
43071
+ rows,
43072
+ localizedTexts,
43073
+ void 0,
43074
+ environment
43075
+ );
43076
+ }
43077
+ }
43078
+ function storedSchemaEntries(context, schemaClass2) {
43079
+ const entries = /* @__PURE__ */ new Map();
43080
+ const visited = /* @__PURE__ */ new Set();
43081
+ let current = schemaClass2;
43082
+ while (current !== void 0 && !visited.has(current.id)) {
43083
+ visited.add(current.id);
43084
+ for (const [schemaKey, memberId] of Object.entries(current.schema)) {
43085
+ if (entries.has(schemaKey)) continue;
43086
+ const child = context.members.get(memberId);
43087
+ if (child === void 0) continue;
43088
+ if (!ownsValueRow(child)) continue;
43089
+ entries.set(schemaKey, child);
43090
+ }
43091
+ current = current.extendsClassId === null ? void 0 : context.classes.get(current.extendsClassId);
43092
+ }
43093
+ return [...entries];
43094
+ }
43095
+ function ownsValueRow(member) {
43096
+ if (member.isReadOnly) return false;
43097
+ return member.kind !== "computed" && member.kind !== "function" && member.kind !== "scriptFunction";
43098
+ }
42987
43099
  function lowerSeedChild(context, member, expression, source, path, rows, localizedTexts, containerId, environment) {
42988
43100
  const symbolId = sourceValueSymbol(context, expression);
42989
43101
  if (symbolId !== null) return symbolId;
@@ -44351,7 +44463,7 @@ function projectMainLocaleFromState(state) {
44351
44463
  }
44352
44464
  function emitValue(context, member, valueId, options) {
44353
44465
  const externalSymbol = context.symbolsByValueId.get(valueId);
44354
- if (!options.exposeIdentity && externalSymbol !== void 0) {
44466
+ if (!options.definitionSite && externalSymbol !== void 0) {
44355
44467
  return externalSymbol;
44356
44468
  }
44357
44469
  if (options.visited.has(valueId)) {
@@ -44375,7 +44487,7 @@ function emitValue(context, member, valueId, options) {
44375
44487
  options.targetTyped ?? false,
44376
44488
  options.environment
44377
44489
  );
44378
- const prefix = options.exposeIdentity ? `@id(${quote4(valueId)})
44490
+ const prefix = options.writeIdentity ? `@id(${quote4(valueId)})
44379
44491
  ` : "";
44380
44492
  return `${prefix}${expression}`;
44381
44493
  }
@@ -44459,7 +44571,8 @@ function classValue(context, member, value, visited, targetTyped, outerEnvironme
44459
44571
  fields.push(
44460
44572
  `${key} = ${emitValue(context, emittedChildMember, childId, {
44461
44573
  environment,
44462
- exposeIdentity: !context.symbolsByValueId.has(childId) && context.referencedValueIds.has(childId),
44574
+ definitionSite: !context.symbolsByValueId.has(childId),
44575
+ writeIdentity: !context.symbolsByValueId.has(childId) && context.referencedValueIds.has(childId),
44463
44576
  visited
44464
44577
  })}`
44465
44578
  );
@@ -44767,7 +44880,8 @@ ${ids.map(
44767
44880
  (id2) => indentNeoSourceNonEmptyLines(
44768
44881
  emitValue(context, entryMember, id2, {
44769
44882
  environment,
44770
- exposeIdentity: context.symbolsByValueId.get(id2) === void 0,
44883
+ definitionSite: context.symbolsByValueId.get(id2) === void 0,
44884
+ writeIdentity: context.symbolsByValueId.get(id2) === void 0,
44771
44885
  visited
44772
44886
  }),
44773
44887
  2
@@ -44793,7 +44907,8 @@ ${entries.flatMap(
44793
44907
  indentNeoSourceNonEmptyLines(
44794
44908
  `${quote4(key)}: ${emitValue(context, entryMember, id2, {
44795
44909
  environment,
44796
- exposeIdentity: !context.symbolsByValueId.has(id2) && context.referencedValueIds.has(id2),
44910
+ definitionSite: !context.symbolsByValueId.has(id2),
44911
+ writeIdentity: !context.symbolsByValueId.has(id2) && context.referencedValueIds.has(id2),
44797
44912
  visited
44798
44913
  })}`,
44799
44914
  2
@@ -45010,6 +45125,7 @@ var init_value_sources = __esm({
45010
45125
  init_lower_members();
45011
45126
  init_source_format();
45012
45127
  init_world_system_classes();
45128
+ init_member_value_id();
45013
45129
  INFERRED_GENERIC_CLASS_PREFIX = "__inferred_class__:";
45014
45130
  MEMBER_KIND_DICTIONARY = 5;
45015
45131
  MEMBER_KIND_LIST = 6;
@@ -45139,7 +45255,8 @@ function validateProjectRootEnvelopeV4(state, analysis) {
45139
45255
  const expression = parseExpression(slot.initializer);
45140
45256
  const annotation2 = expression.kind === "annotated" ? expression.annotations.find((entry) => entry.name === "id") : void 0;
45141
45257
  const authoredValueId = annotation2?.args[0];
45142
- if (authoredValueId?.kind !== "litString" || authoredValueId.value !== memberData.valueId) {
45258
+ if (authoredValueId === void 0) return;
45259
+ if (authoredValueId.kind !== "litString" || authoredValueId.value !== memberData.valueId) {
45143
45260
  throw new Error(
45144
45261
  `Root value root.${expected.name} must retain stable value id ${memberData.valueId}.`
45145
45262
  );
@@ -66474,6 +66591,13 @@ function assignPendingIds(changes, staticValueSeeds, reconstructed3) {
66474
66591
  const assign = (pendingId2) => {
66475
66592
  const existing = assigned.get(pendingId2);
66476
66593
  if (existing !== void 0) return existing;
66594
+ const memberLocator = pendingMemberValueMemberId(pendingId2);
66595
+ if (memberLocator !== null) {
66596
+ const memberId = isPendingId(memberLocator) ? assign(memberLocator) : memberLocator;
66597
+ const derived = derivedMemberValueId(memberId);
66598
+ assigned.set(pendingId2, derived);
66599
+ return derived;
66600
+ }
66477
66601
  const fresh = randomUUID5();
66478
66602
  assigned.set(pendingId2, fresh);
66479
66603
  return fresh;
@@ -66668,10 +66792,6 @@ function readProjectVersionTransactionStatus(value, transactionId) {
66668
66792
  value.errorMessage,
66669
66793
  `Project transaction "${transactionId}" errorMessage`
66670
66794
  );
66671
- const resultCursor = readNullableString(
66672
- value.resultCursor,
66673
- `Project transaction "${transactionId}" resultCursor`
66674
- );
66675
66795
  return {
66676
66796
  transactionId,
66677
66797
  commitStatus: value.commitStatus,
@@ -66692,8 +66812,7 @@ function readProjectVersionTransactionStatus(value, transactionId) {
66692
66812
  `Project transaction "${transactionId}" appliedChunkCount`
66693
66813
  ),
66694
66814
  errorCode,
66695
- errorMessage: errorMessage3,
66696
- resultCursor
66815
+ errorMessage: errorMessage3
66697
66816
  };
66698
66817
  }
66699
66818
  function readProjectVersionTransactionResultPage(value, transactionId) {
@@ -66901,7 +67020,7 @@ async function downloadProjectVersionTransactionResult(args) {
66901
67020
  const assignments = {};
66902
67021
  const changedRecords = [];
66903
67022
  const seenCursors = /* @__PURE__ */ new Set();
66904
- let cursor = args.status.resultCursor ?? "";
67023
+ let cursor = "";
66905
67024
  for (; ; ) {
66906
67025
  if (seenCursors.has(cursor)) {
66907
67026
  throw new Error(
@@ -68496,6 +68615,7 @@ var init_push = __esm({
68496
68615
  init_project_documents();
68497
68616
  init_workspace_status();
68498
68617
  init_lower_support();
68618
+ init_member_value_id();
68499
68619
  init_source_diagnostics();
68500
68620
  init_projection();
68501
68621
  init_project_file_push();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.10.3",
3
+ "version": "0.10.5",
4
4
  "description": "Neo Compose native project-source CLI with bidirectional sync.",
5
5
  "license": "MIT",
6
6
  "type": "module",