@neocompose/cli 0.81.2 → 0.81.4

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,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.81.4
4
+
5
+ ### Patch Changes
6
+
7
+ - 7ed5870: Reject values on boolean flags so `push --dry-run=true` cannot silently perform a real commit. Use bare switches such as `--dry-run` and `--help`.
8
+ - 887fb8c: Reject unused branch flags and unsupported dry-run requests before command execution, so branch creation and other mutating commands cannot silently ignore a requested preview.
9
+ - 6631cef: Refuse initialization into a nonempty directory, apart from Git metadata, so init cannot overwrite existing source or managed files without consent.
10
+ - b2a688d: Resolve source conflicts with CRLF or mixed line endings while preserving the selected source bytes and formatting.
11
+ - 743783d: Refuse branch switches with unpushed changes, conflicts, invalid source, or pending push recovery so switching cannot silently discard local work.
12
+ - b0b945f: Report constructor parameter errors at their source location instead of crashing
13
+ with an internal missing-constructor manifest error during status or push.
14
+ - c11209a: Fix lookup initializers losing row identities in projected selection sets.
15
+ Accept Reference(value) for evaluated class values, including collection
16
+ callbacks, while preserving type checking and the original value identity.
17
+
18
+ ## 0.81.3
19
+
20
+ ### Patch Changes
21
+
22
+ - 9bdbe4b: Preserve outer construction values after editing an inline child. Stored children at their virtual IDs keep the outer construction's omitted fields when their variant selection matches, and ToVariant applies in place without recording an independent applied layer.
23
+ - 45e1b8b: Keep nested generic constructions bound to their own type arguments. Root values such as `Box<Box<int>>` and `Box<List<Box<int>>>` now push and round-trip without constructor argument errors.
24
+ - b6bb4b8: Fix edits to deeply nested projected constructor arguments when a changed field is packed inside its parent. Reconciliation now protects the edit with the physical parent row's content hash.
25
+
3
26
  ## 0.81.2
4
27
 
5
28
  ### Patch Changes
package/dist/neo.mjs CHANGED
@@ -19229,6 +19229,11 @@ function parseArgs(argv) {
19229
19229
  const equals = arg.indexOf("=");
19230
19230
  const name = arg.slice(2, equals < 0 ? void 0 : equals);
19231
19231
  if (equals >= 0) {
19232
+ if (BOOLEAN_FLAGS.has(name)) {
19233
+ throw new NeoCliUsageError(
19234
+ `--${name} does not accept a value. Pass --${name} to enable it, or omit the flag.`
19235
+ );
19236
+ }
19232
19237
  flags.set(name, arg.slice(equals + 1));
19233
19238
  continue;
19234
19239
  }
@@ -19283,6 +19288,38 @@ function assertAllowedFlags(args, allowed) {
19283
19288
  );
19284
19289
  }
19285
19290
  }
19291
+ function assertCommandFlags(args) {
19292
+ const subcommand = args.positional[0];
19293
+ if (args.command === "branch") {
19294
+ const allowed = /* @__PURE__ */ new Set(["api"]);
19295
+ if (subcommand === "list") {
19296
+ allowed.add("json");
19297
+ allowed.add("all");
19298
+ }
19299
+ if (subcommand === "create") allowed.add("from");
19300
+ if (subcommand === "refresh") allowed.add("dry-run");
19301
+ assertAllowedFlags(args, allowed);
19302
+ }
19303
+ if (!args.flags.has("dry-run")) return;
19304
+ switch (args.command) {
19305
+ case "push":
19306
+ case "merge":
19307
+ case "refresh":
19308
+ return;
19309
+ case "branch":
19310
+ if (subcommand === "refresh") return;
19311
+ break;
19312
+ case "release":
19313
+ if (subcommand === "cut") return;
19314
+ break;
19315
+ case "migrate":
19316
+ if (subcommand === "run") return;
19317
+ break;
19318
+ }
19319
+ throw new NeoCliUsageError(
19320
+ `Flag "--dry-run" is not supported by neo ${args.command}${subcommand === void 0 ? "" : ` ${subcommand}`}. No changes were made.`
19321
+ );
19322
+ }
19286
19323
  var NeoCliUsageError, BOOLEAN_FLAGS, KNOWN_FLAGS, TEST_ONLY_FLAGS;
19287
19324
  var init_args = __esm({
19288
19325
  "src/args.ts"() {
@@ -33735,7 +33772,48 @@ var init_strict_resolver = __esm({
33735
33772
  );
33736
33773
  case "call": {
33737
33774
  const receiverValue = this.callReceiver(expression.callee, scope);
33738
- if (expression.callee.kind === "ident" && expression.callee.name === "Reference" && Array.isArray(expression.typeArguments)) {
33775
+ if (expression.callee.kind === "ident" && expression.callee.name === "Reference") {
33776
+ if (expression.args.length === 1 && expression.argumentNames?.[0] == null) {
33777
+ if ((expression.typeArguments?.length ?? 0) > 1) {
33778
+ throw new CompileError(
33779
+ "Reference accepts at most one generic target type.",
33780
+ expression.pos
33781
+ );
33782
+ }
33783
+ const explicit = expression.typeArguments?.[0];
33784
+ const targetType = explicit === void 0 ? void 0 : this.resolveType(explicit);
33785
+ const target = this.resolveExpression(
33786
+ expression.args[0],
33787
+ scope,
33788
+ targetType
33789
+ );
33790
+ const type = targetType ?? target.type;
33791
+ if (type.kind !== "typeParameter" && !(type.kind === "named" && this.project.typeById.get(type.typeId)?.kind === "class")) {
33792
+ throw new CompileError(
33793
+ "Reference(value) requires a class value.",
33794
+ expression.pos
33795
+ );
33796
+ }
33797
+ if (!isNeoScriptTypeAssignable(target.type, type, this.project)) {
33798
+ throw new CompileError(
33799
+ `Reference target ${this.describe(target.type)} is not assignable to ${this.describe(type)}.`,
33800
+ expression.pos
33801
+ );
33802
+ }
33803
+ if (expected !== void 0 && !isNeoScriptTypeAssignable(type, expected, this.project)) {
33804
+ throw new CompileError(
33805
+ `Reference target ${this.describe(type)} is not assignable to ${this.describe(expected)}.`,
33806
+ expression.pos
33807
+ );
33808
+ }
33809
+ return { ...target, type };
33810
+ }
33811
+ if (expression.typeArguments === void 0) {
33812
+ throw new CompileError(
33813
+ 'Reference requires one class value, or Reference<T>(id: "...").',
33814
+ expression.pos
33815
+ );
33816
+ }
33739
33817
  const [referenceTypeArgument] = expression.typeArguments;
33740
33818
  if (expression.typeArguments.length !== 1 || referenceTypeArgument === void 0) {
33741
33819
  throw new CompileError(
@@ -45719,6 +45797,7 @@ function validateExpression(expression, expected, scope, environment, uri, range
45719
45797
  validateReferenceExpression(
45720
45798
  expression,
45721
45799
  expected,
45800
+ scope,
45722
45801
  environment,
45723
45802
  uri,
45724
45803
  range2,
@@ -46496,7 +46575,10 @@ function inferExpressionType(expression, scope, environment) {
46496
46575
  const explicit = expression.typeArguments?.[0];
46497
46576
  if (explicit) return semanticAstType(explicit);
46498
46577
  const target = expression.args[0] ? referenceTargetPath(expression.args[0]) : null;
46499
- return target ? environment.referenceTargets.get(target)?.type : void 0;
46578
+ const symbolType = target ? environment.referenceTargets.get(target)?.type : void 0;
46579
+ if (symbolType !== void 0) return symbolType;
46580
+ const argument2 = expression.args[0];
46581
+ return argument2 === void 0 ? void 0 : inferExpressionType(argument2, scope, environment);
46500
46582
  }
46501
46583
  return environment.functions.get(expression.callee.name)?.[0]?.returnType;
46502
46584
  }
@@ -46544,7 +46626,7 @@ function subscriptKeyType(receiver) {
46544
46626
  if (receiver?.name === "List") return primitiveType("int");
46545
46627
  return void 0;
46546
46628
  }
46547
- function validateReferenceExpression(expression, declared, environment, uri, range2, diagnostics) {
46629
+ function validateReferenceExpression(expression, declared, scope, environment, uri, range2, diagnostics) {
46548
46630
  const expected = declared?.name === "NeoDialogueReference" ? { ...declared, name: "NeoDialogue" } : declared;
46549
46631
  const typeArguments = expression.typeArguments ?? [];
46550
46632
  const names = expression.argumentNames ?? [];
@@ -46632,6 +46714,7 @@ function validateReferenceExpression(expression, declared, environment, uri, ran
46632
46714
  if (path !== null && path.startsWith("root.")) return;
46633
46715
  const target = path ? environment.referenceTargets.get(path) : void 0;
46634
46716
  if (!target) {
46717
+ if (path === null || scope.has(path.split(".")[0])) return;
46635
46718
  malformed(
46636
46719
  "Reference target must be a local value, stored static value, or dialogue class symbol."
46637
46720
  );
@@ -54564,7 +54647,7 @@ var init_registry = __esm({
54564
54647
  "../packages/manifest/schema-contract/registry.mjs"() {
54565
54648
  "use strict";
54566
54649
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
54567
- cliVersion: "0.81.2",
54650
+ cliVersion: "0.81.4",
54568
54651
  projectFileUploadBatchSize: 32,
54569
54652
  documentRecords: {
54570
54653
  member: {
@@ -84200,9 +84283,27 @@ function validateClassConstructorDescriptor(info, ctx, requireEveryRequiredField
84200
84283
  `Cannot construct abstract Class '${schemaClass2.name}'.`
84201
84284
  );
84202
84285
  }
84203
- const genericSlot = ctx.__constructionGenericSlots?.toReversed().find((slot) => slot.classId === classId) ?? ctx.__storedConstructionReplaySlot;
84204
- const slotArguments = genericSlot?.classId === classId ? genericSlot.classArguments : void 0;
84205
- const typeArguments = slotArguments === void 0 ? receiverClosedTypeArguments(info.schemaClassInfo.typeArguments, ctx) : void 0;
84286
+ const typeArguments = receiverClosedTypeArguments(
84287
+ info.schemaClassInfo.typeArguments,
84288
+ ctx
84289
+ );
84290
+ const matchesSlot = (slot) => {
84291
+ if (slot.classId !== classId) return false;
84292
+ if (typeArguments === void 0) return true;
84293
+ const env = cachedInstanceEnv(classId, slot.classArguments, ctx);
84294
+ return Object.entries(typeArguments).every(([parameterId, typeInfo]) => {
84295
+ const binding = env.get(parameterId);
84296
+ if (binding?.kind !== "member") return false;
84297
+ const member = evalMemberById(ctx.vm, binding.memberId);
84298
+ return member !== null && typeInfosInvariantlyEqual(
84299
+ runtimeTypeInfoForMember(member, env, ctx, /* @__PURE__ */ new Set()),
84300
+ typeInfo
84301
+ );
84302
+ });
84303
+ };
84304
+ const genericSlot = ctx.__constructionGenericSlots?.findLast(matchesSlot);
84305
+ const replaySlot = ctx.__storedConstructionReplaySlot;
84306
+ const slotArguments = genericSlot?.classArguments ?? (replaySlot !== void 0 && matchesSlot(replaySlot) ? replaySlot.classArguments : void 0);
84206
84307
  const descriptorCache = evaluatorValueGeneration(ctx).constructorDescriptors;
84207
84308
  const descriptorCacheKey = `${requireEveryRequiredField}:${ctx.storedConstructionReplay === true}:${JSON.stringify(slotArguments ?? typeArguments ?? null)}`;
84208
84309
  const cachedDescriptor = descriptorCache.get(info)?.get(descriptorCacheKey);
@@ -89386,20 +89487,6 @@ var init_variant_value_graph = __esm({
89386
89487
  }
89387
89488
  });
89388
89489
 
89389
- // ../packages/neoscript-runtime/src/declaration-default-source.ts
89390
- function declarationDefaultSource(id2, stored, resolveGraph) {
89391
- if (stored !== null && !isCollapseStampedInstanceRoot(stored)) return stored;
89392
- const graph = resolveGraph(id2);
89393
- return defaultSourceWithListeners(graph?.rowsById.get(id2) ?? stored, graph);
89394
- }
89395
- var init_declaration_default_source = __esm({
89396
- "../packages/neoscript-runtime/src/declaration-default-source.ts"() {
89397
- "use strict";
89398
- init_instance_provenance();
89399
- init_member_change_listeners();
89400
- }
89401
- });
89402
-
89403
89490
  // ../packages/models/src/members/stored-value-placement-index.ts
89404
89491
  function buildStoredValuePlacementIndex(args) {
89405
89492
  return createStoredValuePlacementTraversal(args)();
@@ -89779,6 +89866,20 @@ var init_stored_value_placement_index = __esm({
89779
89866
  }
89780
89867
  });
89781
89868
 
89869
+ // ../packages/neoscript-runtime/src/declaration-default-source.ts
89870
+ function declarationDefaultSource(id2, stored, resolveGraph) {
89871
+ if (stored !== null && !isCollapseStampedInstanceRoot(stored)) return stored;
89872
+ const graph = resolveGraph(id2);
89873
+ return defaultSourceWithListeners(graph?.rowsById.get(id2) ?? stored, graph);
89874
+ }
89875
+ var init_declaration_default_source = __esm({
89876
+ "../packages/neoscript-runtime/src/declaration-default-source.ts"() {
89877
+ "use strict";
89878
+ init_instance_provenance();
89879
+ init_member_change_listeners();
89880
+ }
89881
+ });
89882
+
89782
89883
  // ../packages/neoscript-runtime/src/listener-endpoint-projection.ts
89783
89884
  function projectListenerEndpoint(args) {
89784
89885
  const { descriptor } = args;
@@ -89859,6 +89960,14 @@ var init_overlay_readonly_map = __esm({
89859
89960
  });
89860
89961
 
89861
89962
  // ../packages/neoscript-runtime/src/virtual-instance-graph.ts
89963
+ function isMaterializedSpine(location3) {
89964
+ const stored = location3?.materializedRow;
89965
+ if (stored == null || location3?.parentId == null) return false;
89966
+ const virtual = location3.virtualRow;
89967
+ if (!isLiteralValueContent(stored) || !isLiteralValueContent(virtual))
89968
+ return false;
89969
+ return stored.id === virtual.id && (stored.instanceVariantId ?? null) === (virtual.instanceVariantId ?? null) && (stored.instanceVariantRowValueId ?? null) === (virtual.instanceVariantRowValueId ?? null) && (stored.instanceAppliedVariantId ?? null) === (virtual.instanceAppliedVariantId ?? null) && (stored.instanceAppliedVariantRowValueId ?? null) === (virtual.instanceAppliedVariantRowValueId ?? null);
89970
+ }
89862
89971
  function buildVirtualInstanceMaterializedIndex(args) {
89863
89972
  const rowsById = new Map(
89864
89973
  (args.materializedRows ?? args.document.values).map((row) => [row.id, row])
@@ -91103,6 +91212,7 @@ function resolveStoredInstanceGraphs(args) {
91103
91212
  if (next === void 0 || resolvedRootIds.has(next.root.id)) continue;
91104
91213
  resolvedRootIds.add(next.root.id);
91105
91214
  enqueueStoredRoots(stored.includeRoot(next.root, next.member));
91215
+ if (isMaterializedSpine(resolvedLocations.get(next.root.id))) continue;
91106
91216
  const graph = isMemberClassBase(next.member) ? args.resolveClassGraph?.(next.root, next.member) ?? resolveStoredClassValue({
91107
91217
  evaluateInitializer: args.evaluateInitializer,
91108
91218
  document,
@@ -93168,6 +93278,10 @@ function createHeadlessVirtualInstanceResolver(args) {
93168
93278
  graphByValueId.set(graph.instanceRootId, graph);
93169
93279
  };
93170
93280
  const expandRoot = (rootId, declaredMember) => {
93281
+ const containing = graphByValueId.get(rootId);
93282
+ if (containing !== void 0 && isMaterializedSpine(containing.locationsById.get(rootId))) {
93283
+ return expandRoot(containing.instanceRootId, void 0);
93284
+ }
93171
93285
  const cached = graphRowsByRootId.get(rootId);
93172
93286
  if (cached !== void 0) {
93173
93287
  const reads2 = readsByRootId.get(rootId);
@@ -93257,17 +93371,21 @@ function createHeadlessVirtualInstanceResolver(args) {
93257
93371
  recorder.recordValuePlacementRead();
93258
93372
  recorder.recordValueRead(receiverValueId);
93259
93373
  let placement = placements().get(receiverValueId) ?? null;
93374
+ const roots = [];
93260
93375
  while (placement !== null) {
93261
93376
  recorder.recordValueRead(placement.valueId);
93262
93377
  const raw = rawById.get(placement.valueId);
93263
93378
  if (raw !== void 0 && isLiteralValueContent(raw) && isStoredClassResolutionRoot(raw)) {
93264
- return expandRoot(
93265
- placement.valueId,
93266
- membersById.get(placement.memberId)
93267
- );
93379
+ roots.push(placement);
93268
93380
  }
93269
93381
  placement = placement.parentValueId === null ? null : placements().get(placement.parentValueId) ?? null;
93270
93382
  }
93383
+ let resolved = emptyRows;
93384
+ for (let index = roots.length - 1; index >= 0; index -= 1) {
93385
+ const root2 = roots[index];
93386
+ resolved = expandRoot(root2.valueId, membersById.get(root2.memberId));
93387
+ }
93388
+ if (roots.length > 0) return resolved;
93271
93389
  const root = rawById.get(receiverValueId);
93272
93390
  if (root !== void 0 && typeof root.classId === "string" && isStoredClassResolutionRoot(root)) {
93273
93391
  return expandRoot(receiverValueId, {
@@ -93302,7 +93420,7 @@ function createHeadlessVirtualInstanceResolver(args) {
93302
93420
  const known = graphByValueId.get(valueId);
93303
93421
  if (known?.instanceRootId === valueId) return known;
93304
93422
  const stored = rawById.get(valueId);
93305
- if (known !== void 0 && (stored === void 0 || !isStoredClassResolutionRoot(stored)))
93423
+ if (known !== void 0 && (stored === void 0 || !isStoredClassResolutionRoot(stored) || isMaterializedSpine(known.locationsById.get(valueId))))
93306
93424
  return known;
93307
93425
  virtualInstanceRowsForValue(valueId);
93308
93426
  return graphByValueId.get(valueId) ?? null;
@@ -93913,6 +94031,7 @@ function evaluateMemberInitializer(args) {
93913
94031
  __constructedFieldSchemaKeysByValue: /* @__PURE__ */ new WeakMap(),
93914
94032
  __createdStorageKeyDeclarations: args.storageKeyDeclarations ?? /* @__PURE__ */ new Map()
93915
94033
  };
94034
+ let evaluatedContext = ctx;
93916
94035
  const result = (() => {
93917
94036
  try {
93918
94037
  return evaluateNSGetterWithEffects(
@@ -93921,6 +94040,9 @@ function evaluateMemberInitializer(args) {
93921
94040
  args.argumentValues ?? [],
93922
94041
  {
93923
94042
  exportInitializerValues: true,
94043
+ inspectResult: (_value, runtimeContext) => {
94044
+ evaluatedContext = runtimeContext;
94045
+ },
93924
94046
  // Seeded rows already hold their placement.
93925
94047
  ...args.sessionValues === void 0 ? {
93926
94048
  initializerResult: {
@@ -93946,7 +94068,7 @@ function evaluateMemberInitializer(args) {
93946
94068
  args.member,
93947
94069
  result.value,
93948
94070
  created,
93949
- ctx
94071
+ evaluatedContext
93950
94072
  );
93951
94073
  }
93952
94074
  const rootRow = initializerRootRow(result.value, created);
@@ -94657,6 +94779,7 @@ var init_effective_value_reader = __esm({
94657
94779
  init_core();
94658
94780
  init_members();
94659
94781
  init_packed_value_encoding();
94782
+ init_stored_value_placement_index();
94660
94783
  init_read_class_member();
94661
94784
  init_initializer_materialization();
94662
94785
  init_evaluateInitializer();
@@ -94721,6 +94844,7 @@ var init_effective_value_reader = __esm({
94721
94844
  resolvedDocument;
94722
94845
  indexedDocument;
94723
94846
  materializedIndex;
94847
+ storedPlacements;
94724
94848
  graphCache;
94725
94849
  createNormalizer(rowIdentities) {
94726
94850
  return createTypedValueNormalizer(
@@ -94994,6 +95118,30 @@ var init_effective_value_reader = __esm({
94994
95118
  unorderedListEntryIds: unorderedListEntryIds2
94995
95119
  });
94996
95120
  }
95121
+ const materializedIndex = this.materializedIndex;
95122
+ this.storedPlacements ??= buildStoredValuePlacementIndex({
95123
+ ...this.readDocument,
95124
+ constructors: this.readDocument.constructors ?? [],
95125
+ indexedRows: {
95126
+ valuesById: this.materializedIndex.rowsById,
95127
+ unorderedListEntryIds: (id2) => materializedIndex.unorderedEntryIdsByContainerId.get(id2) ?? []
95128
+ },
95129
+ resolveStaticValueId: (memberId) => this.memberForId(memberId)?.valueId ?? null
95130
+ });
95131
+ const positions = this.storedPlacements.placementByValueId;
95132
+ let parentId = positions.get(valueId)?.parentValueId;
95133
+ while (parentId != null) {
95134
+ const position = positions.get(parentId);
95135
+ if (position === void 0) break;
95136
+ const parentMember = this.memberForId(position.memberId);
95137
+ if (parentMember !== null && isMemberClassBase(parentMember)) {
95138
+ const containingRows = this.resolveGraph(parentId, parentMember);
95139
+ if (isMaterializedSpine(this.locations.get(valueId)))
95140
+ return containingRows;
95141
+ break;
95142
+ }
95143
+ parentId = position.parentValueId;
95144
+ }
94997
95145
  const rows = resolveStoredInstanceGraphs({
94998
95146
  ...this.context,
94999
95147
  readRecorder: this.context.readTracking === void 0 ? void 0 : virtualReadRecorder(this.context.readTracking),
@@ -96886,7 +97034,9 @@ function isNamePath(expression) {
96886
97034
  }
96887
97035
  function isLiteralCall(expression) {
96888
97036
  if (expression.callee.kind === "ident" && expression.callee.name === "Reference") {
96889
- return true;
97037
+ if (expression.argumentNames?.some((name) => name !== null)) return true;
97038
+ const argument2 = expression.args[0];
97039
+ return expression.args.length === 1 && argument2 !== void 0 && (isNamePath(argument2) || argument2.kind === "litString");
96890
97040
  }
96891
97041
  return expression.callee.kind === "member" && expression.callee.name === "Slice";
96892
97042
  }
@@ -97280,21 +97430,15 @@ function lowerClassConstructors(context, declaration, classId, lowerParameterTyp
97280
97430
  const declared = declaration.constructors.map((declared2) => {
97281
97431
  const id2 = declaredConstructorId2(declaration, declared2);
97282
97432
  const base = context.baseConstructors.get(id2);
97283
- const parameters = declared2.parameters.map((parameter4) => {
97284
- const parameterType = lowerParameterType(parameter4.type, declaration);
97285
- return {
97286
- name: parameter4.name,
97287
- type: parameterType,
97288
- default: lowerParameterDefault(
97289
- context,
97290
- parameter4,
97291
- parameterType,
97292
- `${declaration.name}.${declared2.name}(${parameter4.name})`
97293
- ),
97294
- source: span({ source: parameter4.source }),
97295
- selectionSpan: span({ source: parameter4.source })
97296
- };
97297
- });
97433
+ const parameters = declared2.parameters.map(
97434
+ (parameter4) => lowerConstructorParameter(
97435
+ context,
97436
+ parameter4,
97437
+ declaration,
97438
+ lowerParameterType,
97439
+ `${declaration.name}.${declared2.name}(${parameter4.name})`
97440
+ )
97441
+ );
97298
97442
  const baseArguments = lowerBaseArguments(
97299
97443
  context,
97300
97444
  declaration,
@@ -97339,26 +97483,41 @@ function lowerRequiredConstructor(context, declaration, classId, lowerParameterT
97339
97483
  selectionSpan: sourceSpan
97340
97484
  },
97341
97485
  classId,
97342
- arguments: required2.parameters.map((parameter4) => {
97343
- const parameterType = lowerParameterType(parameter4.type, declaration);
97344
- return {
97345
- name: parameter4.name,
97346
- type: parameterType,
97347
- default: lowerParameterDefault(
97348
- context,
97349
- parameter4,
97350
- parameterType,
97351
- `${declaration.name}(${parameter4.name})`
97352
- ),
97353
- source: span({ source: parameter4.source }),
97354
- selectionSpan: span({ source: parameter4.source })
97355
- };
97356
- }),
97486
+ arguments: required2.parameters.map(
97487
+ (parameter4) => lowerConstructorParameter(
97488
+ context,
97489
+ parameter4,
97490
+ declaration,
97491
+ lowerParameterType,
97492
+ `${declaration.name}(${parameter4.name})`
97493
+ )
97494
+ ),
97357
97495
  code: requiredConstructorBody(required2.body, base?.code),
97358
97496
  ...baseArguments.length === 0 ? {} : { baseArguments },
97359
97497
  ...baseInitializerFields.length === 0 ? {} : { baseInitializerFields }
97360
97498
  };
97361
97499
  }
97500
+ function lowerConstructorParameter(context, parameter4, declaration, lowerParameterType, path) {
97501
+ try {
97502
+ const type = lowerParameterType(parameter4.type, declaration);
97503
+ return {
97504
+ name: parameter4.name,
97505
+ type,
97506
+ default: lowerParameterDefault(context, parameter4, type, path),
97507
+ source: span({ source: parameter4.source }),
97508
+ selectionSpan: span({ source: parameter4.source })
97509
+ };
97510
+ } catch (error) {
97511
+ if (error instanceof SchemaSourceError) throw error;
97512
+ throw new SchemaSourceError(
97513
+ error instanceof Error ? error.message : String(error),
97514
+ parameter4.source.uri,
97515
+ parameter4.source.range.start.line + 1,
97516
+ parameter4.source.range.start.character + 1,
97517
+ "schema-lowering"
97518
+ );
97519
+ }
97520
+ }
97362
97521
  function requiredConstructorRecordId(classId) {
97363
97522
  return isPendingId(classId) ? pendingRequiredConstructorId(classId) : derivedRequiredConstructorId(classId);
97364
97523
  }
@@ -97472,6 +97631,7 @@ var init_lower_constructors = __esm({
97472
97631
  init_pending_id();
97473
97632
  init_declared_constructors();
97474
97633
  init_required_constructor_id();
97634
+ init_source_diagnostics();
97475
97635
  init_declared_constructors2();
97476
97636
  init_lower_parameter_defaults();
97477
97637
  init_init_source();
@@ -117871,6 +118031,7 @@ function* prepareServerOwnedSchemaCommitPasses(args) {
117871
118031
  });
117872
118032
  materializePreparedInstanceInitializers({
117873
118033
  document: args.document,
118034
+ physicalValues: args.document.physicalValues,
117874
118035
  prepared,
117875
118036
  contentHashHeads: args.contentHashHeads
117876
118037
  });
@@ -118190,6 +118351,7 @@ function materializePreparedInstanceInitializers(args) {
118190
118351
  args.contentHashHeads,
118191
118352
  "value"
118192
118353
  );
118354
+ let writeBaseHashFor;
118193
118355
  const owners = resolveSemanticOwnerMembersForValues(
118194
118356
  materializationScope.document,
118195
118357
  new Set(sites.keys()),
@@ -118330,7 +118492,14 @@ function materializePreparedInstanceInitializers(args) {
118330
118492
  const existing = existingValueById.get(created.id);
118331
118493
  if (existing !== void 0) {
118332
118494
  if (materializedValueRowsEqual(existing, created)) continue;
118333
- const contentHash = liveValueHashes().get(created.id);
118495
+ writeBaseHashFor ??= createValueWriteBaseHashResolver({
118496
+ document: args.document,
118497
+ physicalValues: args.physicalValues,
118498
+ prepared: args.prepared,
118499
+ contentHashHeads: args.contentHashHeads,
118500
+ source: "server-initializer-materialization"
118501
+ });
118502
+ const contentHash = writeBaseHashFor(created.id);
118334
118503
  if (contentHash === void 0) {
118335
118504
  throw new Error(
118336
118505
  `Cannot reconcile materialized initializer row "${created.id}": its live content hash is unavailable.`
@@ -118345,7 +118514,7 @@ function materializePreparedInstanceInitializers(args) {
118345
118514
  source: "server-initializer-materialization",
118346
118515
  sourceValueId: valueId
118347
118516
  }),
118348
- expectedBaseContentHash: contentHash
118517
+ ...contentHash === null ? {} : { expectedBaseContentHash: contentHash }
118349
118518
  });
118350
118519
  existingValueById.set(created.id, created);
118351
118520
  continue;
@@ -120278,7 +120447,7 @@ function valueIdsFromRows(explicitRows, sweepRows) {
120278
120447
  for (const row of sweepRows) ids.add(row.id);
120279
120448
  return ids;
120280
120449
  }
120281
- function createValueRecompileBaseHashResolver(args) {
120450
+ function createValueWriteBaseHashResolver(args) {
120282
120451
  const hashById = indexLiveContentHashesByRecordId(
120283
120452
  args.contentHashHeads,
120284
120453
  "value"
@@ -120323,7 +120492,7 @@ function createValueRecompileBaseHashResolver(args) {
120323
120492
  nextData: root,
120324
120493
  expectedBaseContentHash: rootHash,
120325
120494
  intent: createProjectVersionIntent("value.update", {
120326
- source: "server-neoscript-recompile"
120495
+ source: args.source
120327
120496
  })
120328
120497
  });
120329
120498
  }
@@ -120441,7 +120610,8 @@ function prepareServerOwnedValueInitializerBodies(args) {
120441
120610
  const currentById = new Map(
120442
120611
  args.document.values.map((value) => [value.id, value])
120443
120612
  );
120444
- const baseHashFor = createValueRecompileBaseHashResolver({
120613
+ const baseHashFor = createValueWriteBaseHashResolver({
120614
+ source: "server-neoscript-recompile",
120445
120615
  document: committedDocument,
120446
120616
  physicalValues: args.document.physicalValues,
120447
120617
  prepared: args.prepared,
@@ -120913,7 +121083,8 @@ function prepareServerOwnedDelegateValueBodies(args) {
120913
121083
  const currentById = new Map(
120914
121084
  args.document.values.map((value) => [value.id, value])
120915
121085
  );
120916
- const baseHashFor = createValueRecompileBaseHashResolver({
121086
+ const baseHashFor = createValueWriteBaseHashResolver({
121087
+ source: "server-neoscript-recompile",
120917
121088
  document: committedDocument,
120918
121089
  physicalValues: args.physicalValues,
120919
121090
  prepared: args.prepared,
@@ -148655,6 +148826,11 @@ function computeStatus(workspace, options, conflictedFiles, parseErrors, parseWa
148655
148826
  Object.values(loweringRecords)
148656
148827
  )
148657
148828
  });
148829
+ if (failedDeclarations.size > 0 && validAnalysis.schema.classes.some(
148830
+ (declaration) => failedDeclarations.has(declaration)
148831
+ )) {
148832
+ return refusedStatus(conflictedFiles, parseErrors, parseWarnings);
148833
+ }
148658
148834
  const loweredMemberIds = new Set(
148659
148835
  manifest.members.map((member) => member.id)
148660
148836
  );
@@ -159686,7 +159862,7 @@ function projectSignatureHelp(analysis, document, position) {
159686
159862
  );
159687
159863
  }
159688
159864
  if (callee.text === "Reference") {
159689
- const parameters = ["symbol or id", "withProvenance = false"];
159865
+ const parameters = ["class value, symbol, or id", "withProvenance = false"];
159690
159866
  return signatureResult(
159691
159867
  "Reference",
159692
159868
  parameters,
@@ -189585,7 +189761,13 @@ var init_exports = {};
189585
189761
  __export(init_exports, {
189586
189762
  runInit: () => runInit
189587
189763
  });
189588
- import { existsSync as existsSync19, mkdirSync as mkdirSync14, readFileSync as readFileSync25, writeFileSync as writeFileSync15 } from "node:fs";
189764
+ import {
189765
+ existsSync as existsSync19,
189766
+ mkdirSync as mkdirSync14,
189767
+ readFileSync as readFileSync25,
189768
+ readdirSync as readdirSync7,
189769
+ writeFileSync as writeFileSync15
189770
+ } from "node:fs";
189589
189771
  import { join as join23, resolve as resolve4 } from "node:path";
189590
189772
  async function runInit(options) {
189591
189773
  let projectId = options.projectId;
@@ -189686,6 +189868,16 @@ async function runInit(options) {
189686
189868
  if (existsSync19(join23(root, NEO_CONFIG_FILE))) {
189687
189869
  throw new Error(`"${join23(root, NEO_CONFIG_FILE)}" already exists.`);
189688
189870
  }
189871
+ if (existsSync19(root)) {
189872
+ const existing = readdirSync7(root).find(
189873
+ (name) => name !== ".git" && name !== ".gitignore"
189874
+ );
189875
+ if (existing !== void 0) {
189876
+ throw new Error(
189877
+ `Cannot initialize ${JSON.stringify(root)} because it contains ${JSON.stringify(existing)}. Choose an empty directory, or back up and move existing files first.`
189878
+ );
189879
+ }
189880
+ }
189689
189881
  mkdirSync14(root, { recursive: true });
189690
189882
  await acquireWorkspaceLock(root, "init");
189691
189883
  ensurePrivateStateIgnored(root);
@@ -193007,7 +193199,7 @@ import { createHash as createHash18, randomUUID as randomUUID8 } from "node:cryp
193007
193199
  import {
193008
193200
  existsSync as existsSync20,
193009
193201
  mkdirSync as mkdirSync15,
193010
- readdirSync as readdirSync7,
193202
+ readdirSync as readdirSync8,
193011
193203
  readFileSync as readFileSync27,
193012
193204
  realpathSync as realpathSync2,
193013
193205
  renameSync as renameSync10,
@@ -194301,7 +194493,7 @@ function testBuildFiles(root) {
194301
194493
  if (!existsSync20(root)) return [];
194302
194494
  const files = [];
194303
194495
  const visit = (directory) => {
194304
- for (const entry of readdirSync7(directory, { withFileTypes: true })) {
194496
+ for (const entry of readdirSync8(directory, { withFileTypes: true })) {
194305
194497
  if (entry.isSymbolicLink()) continue;
194306
194498
  const path = join24(directory, entry.name);
194307
194499
  if (entry.isDirectory()) visit(path);
@@ -197588,6 +197780,47 @@ function findVersion(versions, ref) {
197588
197780
  (version) => version.id === ref || version.name === ref || version.semver.label === ref
197589
197781
  );
197590
197782
  }
197783
+ function assertCleanBranchSwitch(workspace) {
197784
+ assertNoPendingPushRecovery(workspace);
197785
+ const records3 = Object.values(workspace.state.records);
197786
+ for (const record7 of records3) {
197787
+ if (record7.conflictServerHash !== void 0) {
197788
+ throw new Error(
197789
+ "Cannot switch branches with unresolved record conflicts. Run neo resolve before switching."
197790
+ );
197791
+ }
197792
+ }
197793
+ if (records3.length === 0) {
197794
+ const inputs = listProjectExportInputsV4(workspace.root);
197795
+ if (inputs.sources.length === 0 && inputs.binaries.length === 0) return;
197796
+ }
197797
+ const status = computeWorkspaceStatus2(workspace);
197798
+ if (status.conflictedFiles.length > 0) {
197799
+ throw new Error(
197800
+ "Cannot switch branches with unresolved conflicts. Resolve them before switching."
197801
+ );
197802
+ }
197803
+ if (status.parseErrors.length > 0) {
197804
+ throw new Error(
197805
+ "Cannot switch branches while local source has errors. Run neo status and fix or back up your edits before switching."
197806
+ );
197807
+ }
197808
+ if (status.changes.length > 0) {
197809
+ throw new Error(
197810
+ "Cannot switch branches with unpushed changes. Push or back up your edits first; use neo pull --force only to deliberately discard them."
197811
+ );
197812
+ }
197813
+ if (status.authoredValueSeeds.size > 0) {
197814
+ throw new Error(
197815
+ "Cannot switch branches with unpushed value seeds. Push or back up your edits first; use neo pull --force only to deliberately discard them."
197816
+ );
197817
+ }
197818
+ if ((status.binaryChanges?.length ?? 0) > 0) {
197819
+ throw new Error(
197820
+ "Cannot switch branches with pending file changes. Run neo status and settle those changes before switching."
197821
+ );
197822
+ }
197823
+ }
197591
197824
  async function runBranch(workspace, subcommand, positional, fromRef) {
197592
197825
  if (subcommand === "list") {
197593
197826
  const { versions } = await loadVersionMetadata(workspace);
@@ -197678,9 +197911,13 @@ async function runBranch(workspace, subcommand, positional, fromRef) {
197678
197911
  console.log(`Already on ${displayLabel(target)}.`);
197679
197912
  return;
197680
197913
  }
197914
+ assertCleanBranchSwitch(workspace);
197681
197915
  workspace.config = { ...workspace.config, versionId: target.id };
197682
197916
  writeWorkspaceConfig(workspace.root, workspace.config);
197683
- workspace.state = { records: {} };
197917
+ workspace.state = {
197918
+ records: workspace.state.records,
197919
+ localValues: workspace.state.localValues
197920
+ };
197684
197921
  writeWorkspaceState(workspace.root, workspace.state);
197685
197922
  success(
197686
197923
  `Switched to ${color.bold(displayLabel(target))} ${color.dim(`(${target.id})`)}. Pulling\u2026`
@@ -198140,6 +198377,8 @@ var init_branch = __esm({
198140
198377
  init_value_comparison();
198141
198378
  init_ui();
198142
198379
  init_pull();
198380
+ init_workspace_status();
198381
+ init_push_recovery();
198143
198382
  init_http();
198144
198383
  init_project_transaction();
198145
198384
  displayLabel = projectVersionDisplayLabel;
@@ -201165,7 +201404,7 @@ var dev_exports = {};
201165
201404
  __export(dev_exports, {
201166
201405
  runDev: () => runDev
201167
201406
  });
201168
- import { readdirSync as readdirSync8, statSync as statSync6, watch } from "node:fs";
201407
+ import { readdirSync as readdirSync9, statSync as statSync6, watch } from "node:fs";
201169
201408
  import { basename as basename5, dirname as dirname15, join as join27, resolve as resolve6 } from "node:path";
201170
201409
  import { emitKeypressEvents } from "node:readline";
201171
201410
  async function runDev(root, loadWorkspace2, options) {
@@ -201298,7 +201537,7 @@ function countsAsExportInput(root, relativePath, event, directories) {
201298
201537
  function rememberDirectories(root, relativePath, directories) {
201299
201538
  let entries;
201300
201539
  try {
201301
- entries = readdirSync8(join27(root, relativePath), { withFileTypes: true });
201540
+ entries = readdirSync9(join27(root, relativePath), { withFileTypes: true });
201302
201541
  } catch (error) {
201303
201542
  if (error instanceof Error && "code" in error) {
201304
201543
  if (error.code === "ENOENT") return;
@@ -201477,18 +201716,19 @@ function resolveMarkers(source, side) {
201477
201716
  const output = [];
201478
201717
  let mode = "outside";
201479
201718
  for (const line of lines) {
201480
- if (line.startsWith("<<<<<<<")) {
201719
+ const marker = line.endsWith("\r") ? line.slice(0, -1) : line;
201720
+ if (marker.startsWith("<<<<<<<")) {
201481
201721
  if (mode !== "outside") {
201482
201722
  throw new Error("Nested conflict markers are not supported.");
201483
201723
  }
201484
201724
  mode = "local";
201485
201725
  continue;
201486
201726
  }
201487
- if (line === "=======" && mode === "local") {
201727
+ if (marker === "=======" && mode === "local") {
201488
201728
  mode = "server";
201489
201729
  continue;
201490
201730
  }
201491
- if (line.startsWith(">>>>>>>")) {
201731
+ if (marker.startsWith(">>>>>>>")) {
201492
201732
  if (mode !== "server") {
201493
201733
  throw new Error("Unbalanced conflict markers.");
201494
201734
  }
@@ -201775,7 +202015,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
201775
202015
  async function main() {
201776
202016
  const args = parseArgs(process.argv.slice(2));
201777
202017
  if (args.command === "--version") {
201778
- console.log("0.81.2");
202018
+ console.log("0.81.4");
201779
202019
  return;
201780
202020
  }
201781
202021
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
@@ -201787,6 +202027,7 @@ async function main() {
201787
202027
  return;
201788
202028
  }
201789
202029
  assertKnownFlags(args);
202030
+ assertCommandFlags(args);
201790
202031
  if (WORKSPACE_LOCK_COMMANDS.has(args.command)) {
201791
202032
  const root = findWorkspaceRoot(process.cwd());
201792
202033
  if (root !== null) {
@@ -25166,9 +25166,27 @@ function validateClassConstructorDescriptor(info, ctx, requireEveryRequiredField
25166
25166
  `Cannot construct abstract Class '${schemaClass2.name}'.`
25167
25167
  );
25168
25168
  }
25169
- const genericSlot = ctx.__constructionGenericSlots?.toReversed().find((slot) => slot.classId === classId) ?? ctx.__storedConstructionReplaySlot;
25170
- const slotArguments = genericSlot?.classId === classId ? genericSlot.classArguments : void 0;
25171
- const typeArguments = slotArguments === void 0 ? receiverClosedTypeArguments(info.schemaClassInfo.typeArguments, ctx) : void 0;
25169
+ const typeArguments = receiverClosedTypeArguments(
25170
+ info.schemaClassInfo.typeArguments,
25171
+ ctx
25172
+ );
25173
+ const matchesSlot = (slot) => {
25174
+ if (slot.classId !== classId) return false;
25175
+ if (typeArguments === void 0) return true;
25176
+ const env = cachedInstanceEnv(classId, slot.classArguments, ctx);
25177
+ return Object.entries(typeArguments).every(([parameterId, typeInfo]) => {
25178
+ const binding = env.get(parameterId);
25179
+ if (binding?.kind !== "member") return false;
25180
+ const member = evalMemberById(ctx.vm, binding.memberId);
25181
+ return member !== null && typeInfosInvariantlyEqual(
25182
+ runtimeTypeInfoForMember(member, env, ctx, /* @__PURE__ */ new Set()),
25183
+ typeInfo
25184
+ );
25185
+ });
25186
+ };
25187
+ const genericSlot = ctx.__constructionGenericSlots?.findLast(matchesSlot);
25188
+ const replaySlot = ctx.__storedConstructionReplaySlot;
25189
+ const slotArguments = genericSlot?.classArguments ?? (replaySlot !== void 0 && matchesSlot(replaySlot) ? replaySlot.classArguments : void 0);
25172
25190
  const descriptorCache = evaluatorValueGeneration(ctx).constructorDescriptors;
25173
25191
  const descriptorCacheKey = `${requireEveryRequiredField}:${ctx.storedConstructionReplay === true}:${JSON.stringify(slotArguments ?? typeArguments ?? null)}`;
25174
25192
  const cachedDescriptor = descriptorCache.get(info)?.get(descriptorCacheKey);
@@ -32604,6 +32622,14 @@ function materializeMemberDefaultValue(args) {
32604
32622
  }
32605
32623
 
32606
32624
  // ../packages/neoscript-runtime/src/virtual-instance-graph.ts
32625
+ function isMaterializedSpine(location) {
32626
+ const stored = location?.materializedRow;
32627
+ if (stored == null || location?.parentId == null) return false;
32628
+ const virtual = location.virtualRow;
32629
+ if (!isLiteralValueContent(stored) || !isLiteralValueContent(virtual))
32630
+ return false;
32631
+ return stored.id === virtual.id && (stored.instanceVariantId ?? null) === (virtual.instanceVariantId ?? null) && (stored.instanceVariantRowValueId ?? null) === (virtual.instanceVariantRowValueId ?? null) && (stored.instanceAppliedVariantId ?? null) === (virtual.instanceAppliedVariantId ?? null) && (stored.instanceAppliedVariantRowValueId ?? null) === (virtual.instanceAppliedVariantRowValueId ?? null);
32632
+ }
32607
32633
  function buildVirtualInstanceMaterializedIndex(args) {
32608
32634
  const rowsById = new Map(
32609
32635
  (args.materializedRows ?? args.document.values).map((row) => [row.id, row])
@@ -35156,6 +35182,10 @@ function createHeadlessVirtualInstanceResolver(args) {
35156
35182
  graphByValueId.set(graph.instanceRootId, graph);
35157
35183
  };
35158
35184
  const expandRoot = (rootId, declaredMember) => {
35185
+ const containing = graphByValueId.get(rootId);
35186
+ if (containing !== void 0 && isMaterializedSpine(containing.locationsById.get(rootId))) {
35187
+ return expandRoot(containing.instanceRootId, void 0);
35188
+ }
35159
35189
  const cached = graphRowsByRootId.get(rootId);
35160
35190
  if (cached !== void 0) {
35161
35191
  const reads2 = readsByRootId.get(rootId);
@@ -35245,17 +35275,21 @@ function createHeadlessVirtualInstanceResolver(args) {
35245
35275
  recorder.recordValuePlacementRead();
35246
35276
  recorder.recordValueRead(receiverValueId);
35247
35277
  let placement = placements().get(receiverValueId) ?? null;
35278
+ const roots = [];
35248
35279
  while (placement !== null) {
35249
35280
  recorder.recordValueRead(placement.valueId);
35250
35281
  const raw = rawById.get(placement.valueId);
35251
35282
  if (raw !== void 0 && isLiteralValueContent(raw) && isStoredClassResolutionRoot(raw)) {
35252
- return expandRoot(
35253
- placement.valueId,
35254
- membersById.get(placement.memberId)
35255
- );
35283
+ roots.push(placement);
35256
35284
  }
35257
35285
  placement = placement.parentValueId === null ? null : placements().get(placement.parentValueId) ?? null;
35258
35286
  }
35287
+ let resolved = emptyRows;
35288
+ for (let index = roots.length - 1; index >= 0; index -= 1) {
35289
+ const root2 = roots[index];
35290
+ resolved = expandRoot(root2.valueId, membersById.get(root2.memberId));
35291
+ }
35292
+ if (roots.length > 0) return resolved;
35259
35293
  const root = rawById.get(receiverValueId);
35260
35294
  if (root !== void 0 && typeof root.classId === "string" && isStoredClassResolutionRoot(root)) {
35261
35295
  return expandRoot(receiverValueId, {
@@ -35290,7 +35324,7 @@ function createHeadlessVirtualInstanceResolver(args) {
35290
35324
  const known = graphByValueId.get(valueId);
35291
35325
  if (known?.instanceRootId === valueId) return known;
35292
35326
  const stored = rawById.get(valueId);
35293
- if (known !== void 0 && (stored === void 0 || !isStoredClassResolutionRoot(stored)))
35327
+ if (known !== void 0 && (stored === void 0 || !isStoredClassResolutionRoot(stored) || isMaterializedSpine(known.locationsById.get(valueId))))
35294
35328
  return known;
35295
35329
  virtualInstanceRowsForValue(valueId);
35296
35330
  return graphByValueId.get(valueId) ?? null;
@@ -35663,6 +35697,7 @@ function evaluateMemberInitializer(args) {
35663
35697
  __constructedFieldSchemaKeysByValue: /* @__PURE__ */ new WeakMap(),
35664
35698
  __createdStorageKeyDeclarations: args.storageKeyDeclarations ?? /* @__PURE__ */ new Map()
35665
35699
  };
35700
+ let evaluatedContext = ctx;
35666
35701
  const result = (() => {
35667
35702
  try {
35668
35703
  return evaluateNSGetterWithEffects(
@@ -35671,6 +35706,9 @@ function evaluateMemberInitializer(args) {
35671
35706
  args.argumentValues ?? [],
35672
35707
  {
35673
35708
  exportInitializerValues: true,
35709
+ inspectResult: (_value, runtimeContext) => {
35710
+ evaluatedContext = runtimeContext;
35711
+ },
35674
35712
  // Seeded rows already hold their placement.
35675
35713
  ...args.sessionValues === void 0 ? {
35676
35714
  initializerResult: {
@@ -35696,7 +35734,7 @@ function evaluateMemberInitializer(args) {
35696
35734
  args.member,
35697
35735
  result.value,
35698
35736
  created,
35699
- ctx
35737
+ evaluatedContext
35700
35738
  );
35701
35739
  }
35702
35740
  const rootRow = initializerRootRow(result.value, created);
@@ -40039,7 +40077,48 @@ var StrictNeoScriptResolver = class _StrictNeoScriptResolver {
40039
40077
  );
40040
40078
  case "call": {
40041
40079
  const receiverValue = this.callReceiver(expression.callee, scope);
40042
- if (expression.callee.kind === "ident" && expression.callee.name === "Reference" && Array.isArray(expression.typeArguments)) {
40080
+ if (expression.callee.kind === "ident" && expression.callee.name === "Reference") {
40081
+ if (expression.args.length === 1 && expression.argumentNames?.[0] == null) {
40082
+ if ((expression.typeArguments?.length ?? 0) > 1) {
40083
+ throw new CompileError(
40084
+ "Reference accepts at most one generic target type.",
40085
+ expression.pos
40086
+ );
40087
+ }
40088
+ const explicit = expression.typeArguments?.[0];
40089
+ const targetType = explicit === void 0 ? void 0 : this.resolveType(explicit);
40090
+ const target = this.resolveExpression(
40091
+ expression.args[0],
40092
+ scope,
40093
+ targetType
40094
+ );
40095
+ const type = targetType ?? target.type;
40096
+ if (type.kind !== "typeParameter" && !(type.kind === "named" && this.project.typeById.get(type.typeId)?.kind === "class")) {
40097
+ throw new CompileError(
40098
+ "Reference(value) requires a class value.",
40099
+ expression.pos
40100
+ );
40101
+ }
40102
+ if (!isNeoScriptTypeAssignable(target.type, type, this.project)) {
40103
+ throw new CompileError(
40104
+ `Reference target ${this.describe(target.type)} is not assignable to ${this.describe(type)}.`,
40105
+ expression.pos
40106
+ );
40107
+ }
40108
+ if (expected !== void 0 && !isNeoScriptTypeAssignable(type, expected, this.project)) {
40109
+ throw new CompileError(
40110
+ `Reference target ${this.describe(type)} is not assignable to ${this.describe(expected)}.`,
40111
+ expression.pos
40112
+ );
40113
+ }
40114
+ return { ...target, type };
40115
+ }
40116
+ if (expression.typeArguments === void 0) {
40117
+ throw new CompileError(
40118
+ 'Reference requires one class value, or Reference<T>(id: "...").',
40119
+ expression.pos
40120
+ );
40121
+ }
40043
40122
  const [referenceTypeArgument] = expression.typeArguments;
40044
40123
  if (expression.typeArguments.length !== 1 || referenceTypeArgument === void 0) {
40045
40124
  throw new CompileError(
@@ -59559,7 +59638,7 @@ var INITIALIZER_KEY_PLACEHOLDER = new RegExp(
59559
59638
 
59560
59639
  // ../packages/manifest/schema-contract/registry.mjs
59561
59640
  var PROJECT_SCHEMA_CONTRACT = Object.freeze({
59562
- cliVersion: "0.81.2",
59641
+ cliVersion: "0.81.4",
59563
59642
  projectFileUploadBatchSize: 32,
59564
59643
  documentRecords: {
59565
59644
  member: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@neocompose/cli",
3
- "version": "0.81.2",
3
+ "version": "0.81.4",
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.81.2 -->
12
+ <!-- reviewed-through-cli: 0.81.4 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -88,6 +88,10 @@ neo doctor
88
88
  Use `node cli/bin/neo.mjs` while developing the repository. Pass explicit flags
89
89
  and IDs in automation. Non-TTY/CI commands never wait for pickers or confirms.
90
90
 
91
+ `neo init` requires a new or empty destination, apart from `.git` and
92
+ `.gitignore`. It refuses other existing entries before writing anything; choose
93
+ a separate directory when recovering a checkout whose config is missing.
94
+
91
95
  Require `formatVersion: 4` in `neo.json`. There is no format-3 reader or
92
96
  upgrader. Reconstruct an authoritative format-4 checkout with `neo init`.
93
97
 
@@ -129,7 +129,7 @@ wrappers.
129
129
  The marker near the top of `SKILL.md` must exactly match the package version:
130
130
 
131
131
  ```html
132
- <!-- reviewed-through-cli: 0.81.2 -->
132
+ <!-- reviewed-through-cli: 0.81.4 -->
133
133
  ```
134
134
 
135
135
  The quoted version above is checked too, so this instruction cannot go stale
@@ -29,6 +29,11 @@ neo push
29
29
  neo push --dry-run --force-recompile
30
30
  ```
31
31
 
32
+ `neo status` reports invalid constructor parameters at their `.neo` file, line,
33
+ and column. Fix the reported parameter type before pushing. For example,
34
+ constructor parameters use `Item`; `Reference<Item>` declares member storage.
35
+ A failed class or constructor declaration blocks candidate preparation.
36
+
32
37
  Use `neo pull --regenerate-source-names` to replace generated default dialogue
33
38
  names with readable ones. It keeps chosen names and rewrites references. Use
34
39
  `--reset` to regenerate canonical source and managed binaries. Reset retains
@@ -151,6 +156,9 @@ become real only after acceptance.
151
156
 
152
157
  ## Conflicts and files
153
158
 
159
+ Conflict resolution accepts LF, CRLF, and mixed line endings. It retains the
160
+ selected source lines and their existing line endings.
161
+
154
162
  Resolve source conflicts by editing the final intended source. Use
155
163
  `neo resolve --mine` or `neo resolve --theirs` only for a deliberate whole-side
156
164
  choice. Pull, inspect, dry-run, and retry; there is no force-CAS path.
@@ -228,6 +236,11 @@ neo history status --run <run-id>
228
236
  neo history resume --run <run-id>
229
237
  ```
230
238
 
239
+ Branch switching requires a clean working copy. Unpushed source or binary
240
+ changes, unresolved conflicts, source errors, and pending push recovery stop the
241
+ switch before it changes the selected version or baseline. Push or back up edits
242
+ first; use `neo pull --force` only when discarding them is intended.
243
+
231
244
  Treat branches as copy-on-write forks and releases as immutable snapshots. The
232
245
  server derives the minimum compatibility bump; a caller may only raise it.
233
246
  Require the release login profile for release operations.
@@ -294,6 +307,12 @@ so concurrent environments sharing one OS user cannot read, overwrite, or
294
307
  delete each other's tokens. Login, logout, and authenticated commands all
295
308
  honor both variables.
296
309
 
310
+ Boolean flags are switches: pass `--dry-run` or `--help` without a value.
311
+ Values such as `--dry-run=true` are rejected before a command runs.
312
+ `--dry-run` is supported by push, merge, refresh, branch refresh, release cut,
313
+ and migrate run. Other commands reject it before taking action. Branch options
314
+ are scoped to their subcommands; branch create does not support dry-run.
315
+
297
316
  Pass explicit project/version IDs and flags in automation. Prefer `--json` for
298
317
  machine-readable output and do not depend on interactive pickers or confirms.
299
318
 
@@ -247,6 +247,9 @@ initializer as its default. Omitted fields remain virtual and resolve through
247
247
  the instance's recorded constructor or variant. They track later source-default
248
248
  edits until that field is explicitly overridden; resetting an override makes it
249
249
  virtual again.
250
+ Editing an inline child preserves the outer construction's call-site values for
251
+ its other fields. The child keeps that construction while its variant selection
252
+ matches; selecting a different variant gives it an independent recipe.
250
253
 
251
254
  Declare a lookup member as `Reference<T>`. `public Reference<Config> Config`
252
255
  selects one row and reads as `Config`. `public Set<Reference<Config>> Configs`
@@ -578,6 +581,12 @@ selection. Constructor arguments and member initializers must select compatible
578
581
  targets too. An `NPC` lookup cannot select from `List<Quest>`, even when its
579
582
  initializer is typed `NPC`; status reports the conflicting collection annotation.
580
583
 
584
+ Generic constructions can nest the same generic class, including through
585
+ collections, such as `Box<Box<int>>` or `Box<List<Box<int>>>`. Each `new` uses
586
+ its own type arguments during first push and stored replay. Editing a nested
587
+ constructor argument rebuilds its projected fields in place, including fields
588
+ stored inside a packed parent; push followed by pull leaves a clean checkout.
589
+
581
590
  Schema export retains a constructed default's generic arguments, including
582
591
  nested collection references. For example, a `NeoAnimationClip<ArmPart>`
583
592
  default constructs `new NeoAnimationClip<ArmPart>` and its frames reference
@@ -180,6 +180,22 @@ is one such row — its identity comes from the schema key its parameter settles
180
180
 
181
181
  ## Same-push references
182
182
 
183
+ `Reference<T>` and `Set<Reference<T>>` declare lookup storage. Their runtime
184
+ values are `T` and `Set<T>`, so a projection can assign existing class values
185
+ directly:
186
+
187
+ ```neo
188
+ EligibleIngredients = root.Assets.Crafting.FurnaceRecipes
189
+ .Select((MachineRecipe recipe) => recipe.Ingredient)
190
+ .ToSet()
191
+ ```
192
+
193
+ Writing `Reference(recipe.Ingredient)` inside the callback is also valid.
194
+ `Reference(value)` preserves an evaluated class value and its identity; it does
195
+ not clone it or turn it into a separate wrapper object. An optional generic
196
+ argument checks the target type. Lookup assignment still requires an existing
197
+ backing row and enforces its declared target and collection constraints.
198
+
183
199
  Use the `Reference` intrinsic. Prefer the most readable form the target
184
200
  supports:
185
201
 
@@ -197,9 +213,8 @@ Reference<NeoDialogue>(id: "capitol-dialogue-id")
197
213
  - Use `key:` only for a collection with a unique index. Omit `index:` when the
198
214
  collection has exactly one usable unique index; supply the bare member name
199
215
  when it has more than one.
200
- - Use the generic `id:` form when the ID is the only target information. A
201
- `Reference` call whose argument is neither `id:` nor `key:` is rejected by
202
- name rather than read as an ID.
216
+ - Use the generic `id:` form when the ID is the only target information.
217
+ Unrecognized named arguments are rejected rather than read as IDs.
203
218
  - Inside executable NeoScript, add `withProvenance: true` when the ID names an
204
219
  authored row and the reference must select its nearest `sourceValueId` clone
205
220
  in the lexical receiver's ownership graph. The parameter defaults to `false`;