@neocompose/cli 0.36.1 → 0.36.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,37 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.36.3] - 2026-08-19
4
+
5
+ ### Fixed
6
+
7
+ - Preserve nullable List and Dictionary entry declarations in persisted
8
+ compiler contexts, and keep abstract stored collection fields writable
9
+ through the concrete subclass storage that implements them. Indexed
10
+ `is`-pattern reads and writes such as `Stacks[index] = null` now agree in
11
+ source analysis, IntelliSense, `neo script check --all`, and push replay.
12
+ - Run `neo script check --all` against one locally reconstructed prospective
13
+ project document instead of fetching a stale server document through a
14
+ separate path. New records, managed files, dialogues, and stored bodies are
15
+ validated with the same source transaction visible to local member bodies.
16
+ - Compile canonical `Reference<NeoImage>(id: ...).Slice(...)` and
17
+ `Reference<NeoAudioClip>(id: ...)` file fallbacks when a pulled file has no
18
+ registry symbol, and accept `List.Repeat<T>(value, count)` when an explicit
19
+ entry type is needed for values such as `null`.
20
+ - Include same-transaction texture and audio import-template changes in the
21
+ prospective document before validating managed-file defaults.
22
+
23
+ ## [0.36.2] - 2026-08-19
24
+
25
+ ### Fixed
26
+
27
+ - Report invalid ordinary member initializers at their authored file and token
28
+ in the CLI and editor. C# numeric suffixes such as `0f` now explain that the
29
+ suffix should be removed instead of surfacing as `<project>:1:1` with an EOF
30
+ parser error.
31
+ - Accept `null` as the literal default of a nullable `NeoDelegate`, preventing
32
+ valid pending delegate members from failing prospective project,
33
+ initializer-materialization, and animation validation.
34
+
3
35
  ## [0.36.1] - 2026-08-19
4
36
 
5
37
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -1278,6 +1278,12 @@ function neoScriptRegistryEntryType(typeName) {
1278
1278
  }
1279
1279
  return REGISTRY_ENTRY_TYPES[typeName] ?? null;
1280
1280
  }
1281
+ function neoScriptRegistryReferenceType(entryTypeName) {
1282
+ for (const definition2 of Object.values(REGISTRY_ENTRY_TYPES)) {
1283
+ if (definition2.entryType === entryTypeName) return definition2.refType;
1284
+ }
1285
+ return null;
1286
+ }
1281
1287
  function parameter(name, type) {
1282
1288
  return { name, type };
1283
1289
  }
@@ -11398,6 +11404,19 @@ var init_strict_resolver = __esm({
11398
11404
  expression.pos
11399
11405
  );
11400
11406
  }
11407
+ if (isPrimitive(referencedType, "ImageRef") || isPrimitive(referencedType, "AudioClipRef")) {
11408
+ if (hasProvenanceArgument) {
11409
+ throw new CompileError(
11410
+ `Reference<${referenceTypeArgument.kind === "named" ? referenceTypeArgument.name : "file"}> does not support withProvenance because a project file reference is a file id, not a value-row reference.`,
11411
+ expression.pos
11412
+ );
11413
+ }
11414
+ return literal(
11415
+ referencedType,
11416
+ expression.args[0].value,
11417
+ this.project
11418
+ );
11419
+ }
11401
11420
  return {
11402
11421
  pointer: {
11403
11422
  type: "reference" /* Reference */,
@@ -11407,6 +11426,37 @@ var init_strict_resolver = __esm({
11407
11426
  type: referencedType
11408
11427
  };
11409
11428
  }
11429
+ if (expression.typeArguments && expression.callee.kind === "member" && expression.callee.receiver.kind === "ident" && expression.callee.receiver.name === NEOSCRIPT_LIST_TYPE_NAME) {
11430
+ if (expression.callee.name !== NEOSCRIPT_LIST_REPEAT_NAME) {
11431
+ throw new CompileError(
11432
+ `'${NEOSCRIPT_LIST_TYPE_NAME}' has no generic static member '${expression.callee.name}'.`,
11433
+ expression.pos
11434
+ );
11435
+ }
11436
+ if (expression.typeArguments.length !== 1) {
11437
+ throw new CompileError(
11438
+ `${NEOSCRIPT_LIST_TYPE_NAME}.${NEOSCRIPT_LIST_REPEAT_NAME}<T> requires exactly one entry type argument.`,
11439
+ expression.pos
11440
+ );
11441
+ }
11442
+ const entryTypeArgument = expression.typeArguments[0];
11443
+ if (entryTypeArgument === void 0) {
11444
+ throw new CompileError(
11445
+ `${NEOSCRIPT_LIST_TYPE_NAME}.${NEOSCRIPT_LIST_REPEAT_NAME}<T> requires exactly one entry type argument.`,
11446
+ expression.pos
11447
+ );
11448
+ }
11449
+ return this.resolveListStaticCall(
11450
+ expression.callee.name,
11451
+ expression.args,
11452
+ scope,
11453
+ {
11454
+ kind: "list",
11455
+ elementType: this.resolveType(entryTypeArgument)
11456
+ },
11457
+ expression.pos
11458
+ );
11459
+ }
11410
11460
  if (expression.typeArguments) {
11411
11461
  throw new CompileError(
11412
11462
  "Generic intrinsic calls require a declaration-aware project source context.",
@@ -15209,11 +15259,17 @@ var init_strict_resolver = __esm({
15209
15259
  */
15210
15260
  assertActionControlAccessible(action, operation, pos) {
15211
15261
  const symbol = action.symbol;
15212
- const owner = symbol === void 0 ? void 0 : symbol.inheritedFrom ? this.project.typeById.get(symbol.inheritedFrom.typeId) : [...this.project.typeById.values()].find(
15213
- (type) => type.members.some(
15214
- (candidate) => candidate.id === symbol.id && candidate.inheritedFrom === void 0
15215
- )
15216
- );
15262
+ let owner = symbol?.inheritedFrom ? this.project.typeById.get(symbol.inheritedFrom.typeId) : void 0;
15263
+ if (symbol !== void 0 && symbol.inheritedFrom === void 0) {
15264
+ for (const type of this.project.typeById.values()) {
15265
+ if (type.members.some(
15266
+ (candidate) => candidate.id === symbol.id && candidate.inheritedFrom === void 0
15267
+ )) {
15268
+ owner = type;
15269
+ break;
15270
+ }
15271
+ }
15272
+ }
15217
15273
  if (symbol !== void 0 && owner !== void 0 && memberAccessFailure({
15218
15274
  project: this.project,
15219
15275
  context: this.context,
@@ -15535,6 +15591,11 @@ var init_strict_resolver = __esm({
15535
15591
  resolved = { kind: "primitive", name: builtin };
15536
15592
  break;
15537
15593
  }
15594
+ const registryReference = neoScriptRegistryReferenceType(type.name);
15595
+ if (registryReference) {
15596
+ resolved = registryReference;
15597
+ break;
15598
+ }
15538
15599
  const named = this.project.typeByName.get(type.name);
15539
15600
  if (!named)
15540
15601
  throw new CompileError(`Unknown type '${type.name}'`, type.pos);
@@ -22973,7 +23034,15 @@ function validateDeclaration(uri, declaration, documentKind, environment, diagno
22973
23034
  scope.set(member.name, semanticType(member.type));
22974
23035
  }
22975
23036
  for (const member of declaration.members) {
22976
- validateMember(uri, member, documentKind, scope, environment, diagnostics);
23037
+ validateMember(
23038
+ uri,
23039
+ member,
23040
+ declaration.name,
23041
+ documentKind,
23042
+ scope,
23043
+ environment,
23044
+ diagnostics
23045
+ );
22977
23046
  }
22978
23047
  if (declaration.kind === "class") {
22979
23048
  validateRetiredParameterReads(uri, declaration, scope, diagnostics);
@@ -23243,7 +23312,7 @@ function slotPosition(start, pos) {
23243
23312
  }
23244
23313
  return { line: start.line + pos.line - 1, character: pos.column - 1 };
23245
23314
  }
23246
- function validateMember(uri, member, documentKind, ownerScope, environment, diagnostics) {
23315
+ function validateMember(uri, member, ownerName, documentKind, ownerScope, environment, diagnostics) {
23247
23316
  const memberType2 = semanticType(member.type);
23248
23317
  validateAnnotations(
23249
23318
  uri,
@@ -23285,7 +23354,11 @@ function validateMember(uri, member, documentKind, ownerScope, environment, diag
23285
23354
  environment,
23286
23355
  member.initializer.range,
23287
23356
  diagnostics,
23288
- anchorOf(member.initializer)
23357
+ anchorOf(member.initializer),
23358
+ {
23359
+ code: "invalid-project-member-initializer",
23360
+ label: `Member '${ownerName}.${member.name}' initializer`
23361
+ }
23289
23362
  );
23290
23363
  }
23291
23364
  if (documentKind === "flow" && member.flowInitializer?.body?.kind === "block") {
@@ -23789,7 +23862,7 @@ function settingsClassFields(name) {
23789
23862
  function anchorOf(source) {
23790
23863
  return { text: source.text, start: source.range.start };
23791
23864
  }
23792
- function validateExpressionText(uri, text, expected, scope, environment, range2, diagnostics, anchor) {
23865
+ function validateExpressionText(uri, text, expected, scope, environment, range2, diagnostics, anchor, syntaxDiagnostic) {
23793
23866
  try {
23794
23867
  validateExpression(
23795
23868
  parseExpression(text),
@@ -23803,7 +23876,37 @@ function validateExpressionText(uri, text, expected, scope, environment, range2,
23803
23876
  );
23804
23877
  } catch (error) {
23805
23878
  if (!(error instanceof CompileError)) throw error;
23879
+ if (syntaxDiagnostic !== void 0) {
23880
+ const errorRange = anchor === void 0 ? range2 : anchorSpan(
23881
+ anchor,
23882
+ { line: error.line, column: error.column },
23883
+ anchorOffset(anchor, error) < text.length ? 1 : 0
23884
+ );
23885
+ pushDiagnostic(
23886
+ diagnostics,
23887
+ uri,
23888
+ errorRange,
23889
+ syntaxDiagnostic.code,
23890
+ memberInitializerSyntaxMessage(syntaxDiagnostic.label, text, error)
23891
+ );
23892
+ return;
23893
+ }
23894
+ }
23895
+ }
23896
+ function memberInitializerSyntaxMessage(label, text, error) {
23897
+ const anchor = {
23898
+ text,
23899
+ start: { line: 0, character: 0 }
23900
+ };
23901
+ const offset = anchorOffset(anchor, error);
23902
+ const suffix = text[offset];
23903
+ const previous = text[offset - 1];
23904
+ const next = text[offset + 1];
23905
+ if (suffix !== void 0 && previous !== void 0 && /[fFdDmM]/u.test(suffix) && /[0-9]/u.test(previous) && (next === void 0 || !/[A-Za-z0-9_]/u.test(next))) {
23906
+ return `${label} uses unsupported C# numeric suffix '${suffix}'. NeoScript numeric literals do not use suffixes; remove '${suffix}'.`;
23806
23907
  }
23908
+ const detail = error.message.replace(/^\d+:\d+:\s*/u, "");
23909
+ return `${label} could not be parsed: ${detail}`;
23807
23910
  }
23808
23911
  function validateFunctionBody(uri, text, returnType, scope, environment, range2, diagnostics) {
23809
23912
  try {
@@ -32561,10 +32664,8 @@ function attachConstructorSignature(type, environment) {
32561
32664
  type.id,
32562
32665
  environment
32563
32666
  );
32564
- if ([...genericEnvironment.values()].some(
32565
- (binding) => binding.kind === "unbound"
32566
- )) {
32567
- return type;
32667
+ for (const binding of genericEnvironment.values()) {
32668
+ if (binding.kind === "unbound") return type;
32568
32669
  }
32569
32670
  const constructorSignature2 = buildNeoScriptConstructorSignature(
32570
32671
  type.id,
@@ -32897,7 +32998,7 @@ function memberSymbol(member, schemaKey, environment, field) {
32897
32998
  writable: false
32898
32999
  };
32899
33000
  }
32900
- const writable = isReadOnly ? false : kind === "computed" ? computedHasSetter(member, field) : modifier !== "abstract" && member.locked !== true;
33001
+ const writable = isReadOnly ? false : kind === "computed" ? computedHasSetter(member, field) : member.locked !== true;
32901
33002
  const storage = typeof member.storage === "string" ? member.storage : void 0;
32902
33003
  const writability = isReadOnly ? "readOnly" : kind === "computed" ? writable ? "setter" : "readOnly" : !writable ? "readOnly" : storage === "immutable" || storage === "save" || storage === "session" ? storage : isStatic && ownerClassId ? staticDefaultWritability(ownerClassId, environment) : void 0;
32903
33004
  const lookupCollectionMemberId = kind === "lookup" ? string2(member.collectionMemberId, `${field}.collectionMemberId`) : null;
@@ -33433,9 +33534,12 @@ function memberType(member, environment, visiting, field) {
33433
33534
  };
33434
33535
  }
33435
33536
  if (kind === "variant") {
33436
- const variantClass = [...environment.classes.values()].find(
33437
- (candidate) => candidate.name === NEO_VARIANT_TYPE_NAME
33438
- );
33537
+ let variantClass;
33538
+ for (const candidate of environment.classes.values()) {
33539
+ if (candidate.name !== NEO_VARIANT_TYPE_NAME) continue;
33540
+ variantClass = candidate;
33541
+ break;
33542
+ }
33439
33543
  if (variantClass === void 0) return { ...unknownType(), nullable };
33440
33544
  return {
33441
33545
  kind: "named",
@@ -44538,6 +44642,7 @@ function isMemberDelegateBase(value) {
44538
44642
  if (defaultValue === void 0 || defaultValue === null) return true;
44539
44643
  if (!isMemberValueBase(defaultValue)) return false;
44540
44644
  if (isInitValueContent(defaultValue)) return true;
44645
+ if (defaultValue.value === null) return v.required === false;
44541
44646
  return isNSDelegateValue(defaultValue.value);
44542
44647
  }
44543
44648
  function nsActionListenerIdentity(listener) {
@@ -61723,7 +61828,8 @@ function interfaceToLanguageType(neoInterface, context) {
61723
61828
  closure = [neoInterface];
61724
61829
  }
61725
61830
  const members = /* @__PURE__ */ new Map();
61726
- for (const source of [...closure].reverse()) {
61831
+ for (let index = closure.length - 1; index >= 0; index -= 1) {
61832
+ const source = closure[index];
61727
61833
  for (const key of getInterfaceMemberKeyOrder(source)) {
61728
61834
  const member = source.members[key];
61729
61835
  if (!member) continue;
@@ -61970,13 +62076,11 @@ function memberRuntimeType(record3, context, seen = /* @__PURE__ */ new Set(), g
61970
62076
  const projectedEntry = entry ? substituteForGenericEnvironment(entry, genericEnvironment, context) : null;
61971
62077
  return {
61972
62078
  kind: "list",
61973
- elementType: projectedEntry ? requiredType(
61974
- memberRuntimeType(
61975
- projectedEntry,
61976
- context,
61977
- nextSeen,
61978
- genericEnvironment
61979
- )
62079
+ elementType: projectedEntry ? memberRuntimeType(
62080
+ projectedEntry,
62081
+ context,
62082
+ nextSeen,
62083
+ genericEnvironment
61980
62084
  ) : UNKNOWN_TYPE2,
61981
62085
  listMemberId: record3.id,
61982
62086
  nullable: !required2
@@ -61989,13 +62093,11 @@ function memberRuntimeType(record3, context, seen = /* @__PURE__ */ new Set(), g
61989
62093
  return {
61990
62094
  kind: "dictionary",
61991
62095
  keyType: member.keyKind === "enum" && member.keyEnumId ? namedType(member.keyEnumId, true) : primitive2("string", true),
61992
- valueType: projectedEntry ? requiredType(
61993
- memberRuntimeType(
61994
- projectedEntry,
61995
- context,
61996
- nextSeen,
61997
- genericEnvironment
61998
- )
62096
+ valueType: projectedEntry ? memberRuntimeType(
62097
+ projectedEntry,
62098
+ context,
62099
+ nextSeen,
62100
+ genericEnvironment
61999
62101
  ) : UNKNOWN_TYPE2,
62000
62102
  nullable: !required2
62001
62103
  };
@@ -62994,7 +63096,10 @@ function initializerReferencesLexicalThis(source) {
62994
63096
  }
62995
63097
  function initializerReferencesAnyIdentifier(source, names) {
62996
63098
  const identifiers = initializerReferencedIdentifiers(source);
62997
- return [...names].some((name) => identifiers.has(name));
63099
+ for (const name of names) {
63100
+ if (identifiers.has(name)) return true;
63101
+ }
63102
+ return false;
62998
63103
  }
62999
63104
  function compileNSPropertyBodies(args) {
63000
63105
  if (args.member.kind !== 10 /* NSProperty */) return;
@@ -63596,7 +63701,7 @@ function declaredConstructorsOfClass(constructors, schemaClass2) {
63596
63701
  });
63597
63702
  }
63598
63703
  function argumentNameSetKey2(names) {
63599
- return [...names].map((name) => name.toLowerCase()).sort().join(", ");
63704
+ return names.map((name) => name.toLowerCase()).sort().join(", ");
63600
63705
  }
63601
63706
  function describeConstructorOverloads(constructors) {
63602
63707
  return constructors.map(
@@ -63696,7 +63801,7 @@ function resolveMemberDeclaredTypeInfoInternal(member, members, seen) {
63696
63801
  return {
63697
63802
  type: resolved.kind,
63698
63803
  required: resolved.required,
63699
- entryTypeInfo: { ...entryTypeInfo, required: true },
63804
+ entryTypeInfo,
63700
63805
  ...isMemberDictionaryBase(resolved) && resolved.keyEnumId ? { keyEnumId: resolved.keyEnumId } : {}
63701
63806
  };
63702
63807
  }
@@ -91957,7 +92062,12 @@ function materializeAuthoredListItems(args) {
91957
92062
  args.member.entryMemberId
91958
92063
  );
91959
92064
  const unordered = listKindOf(args.member) === "unordered";
91960
- const ids = unordered ? [...args.authoredRows.values()].filter((row) => row.containerId === args.row.id).map((row) => row.id) : args.row.value;
92065
+ const ids = unordered ? [] : args.row.value;
92066
+ if (unordered) {
92067
+ for (const row of args.authoredRows.values()) {
92068
+ if (row.containerId === args.row.id) ids.push(row.id);
92069
+ }
92070
+ }
91961
92071
  const materializedIds = [];
91962
92072
  for (const childId of ids) {
91963
92073
  if (typeof childId !== "string") {
@@ -92216,6 +92326,12 @@ function initBackedValueRow(value) {
92216
92326
  if (!isMemberValue(value)) return null;
92217
92327
  return isInitValueContent(value) ? value : null;
92218
92328
  }
92329
+ function valueIdsFromRows(explicitRows, sweepRows) {
92330
+ const ids = /* @__PURE__ */ new Set();
92331
+ for (const row of explicitRows) ids.add(row.id);
92332
+ for (const row of sweepRows) ids.add(row.id);
92333
+ return ids;
92334
+ }
92219
92335
  function prepareServerOwnedValueInitializerBodies(args) {
92220
92336
  const explicitRows = /* @__PURE__ */ new Map();
92221
92337
  for (let index = 0; index < args.prepared.length; index += 1) {
@@ -92237,10 +92353,7 @@ function prepareServerOwnedValueInitializerBodies(args) {
92237
92353
  args.postDocument,
92238
92354
  args.prepared
92239
92355
  );
92240
- const targetIds = /* @__PURE__ */ new Set([
92241
- ...[...explicitRows.values()].map((row) => row.id),
92242
- ...sweepRows.map((row) => row.id)
92243
- ]);
92356
+ const targetIds = valueIdsFromRows(explicitRows.values(), sweepRows);
92244
92357
  const valuesById = new Map(
92245
92358
  committedDocument.values.map((value) => [value.id, value])
92246
92359
  );
@@ -92367,10 +92480,7 @@ function prepareServerOwnedDelegateValueBodies(args) {
92367
92480
  args.postDocument,
92368
92481
  args.prepared
92369
92482
  );
92370
- const targetIds = /* @__PURE__ */ new Set([
92371
- ...[...explicitRows.values()].map((row) => row.id),
92372
- ...sweepRows.map((row) => row.id)
92373
- ]);
92483
+ const targetIds = valueIdsFromRows(explicitRows.values(), sweepRows);
92374
92484
  const rootOwnerByValueId = /* @__PURE__ */ new Map();
92375
92485
  const delegateScope = withVariantOwnershipRoots(
92376
92486
  committedDocument,
@@ -92921,6 +93031,20 @@ function applyProjectVersionWriteChanges(document, changes) {
92921
93031
  changes,
92922
93032
  "project-file"
92923
93033
  ),
93034
+ // Import-setting templates are part of the same file graph. A new file
93035
+ // may select a new template in one source transaction, and default-value
93036
+ // materialization validates that relationship immediately against this
93037
+ // post-write document.
93038
+ textureTemplates: applyArrayChanges(
93039
+ document.textureTemplates ?? [],
93040
+ changes,
93041
+ "unity-texture-template"
93042
+ ),
93043
+ audioClipTemplates: applyArrayChanges(
93044
+ document.audioClipTemplates ?? [],
93045
+ changes,
93046
+ "unity-audio-clip-template"
93047
+ ),
92924
93048
  internalRecordRelations: applyArrayChanges(
92925
93049
  document.internalRecordRelations ?? [],
92926
93050
  changes,
@@ -94841,7 +94965,7 @@ function listVirtualProjectSourceFilesV4(files) {
94841
94965
  const kind = neoProjectSourceKind(file.path);
94842
94966
  return kind !== null && isNeoProjectProductionSourceKind(kind) && !neoProjectPathHasIgnoredDirectory(file.path);
94843
94967
  });
94844
- const sorted = [...selected].sort(
94968
+ const sorted = selected.sort(
94845
94969
  (left, right) => compareWorkspacePaths(left.path, right.path)
94846
94970
  );
94847
94971
  for (let index = 1; index < sorted.length; index += 1) {
@@ -95482,14 +95606,24 @@ function computeWorkspaceStatus(workspace, options) {
95482
95606
  (binary) => binary.action !== "unchanged" && binary.action !== "converged"
95483
95607
  );
95484
95608
  reportPhase("binary-inspection");
95485
- const invalidatedFileIds = /* @__PURE__ */ new Set([
95486
- ...binaryFiles.filter(
95487
- (binary) => binary.action === "create" || binary.action === "upload"
95488
- ).map((binary) => binary.fileId),
95489
- ...options.trustedPendingProjectFiles?.keys() ?? []
95490
- ]);
95609
+ const invalidatedFileIds = /* @__PURE__ */ new Set();
95610
+ for (const binary of binaryFiles) {
95611
+ if (binary.action === "create" || binary.action === "upload") {
95612
+ invalidatedFileIds.add(binary.fileId);
95613
+ }
95614
+ }
95615
+ for (const fileId of options.trustedPendingProjectFiles?.keys() ?? []) {
95616
+ invalidatedFileIds.add(fileId);
95617
+ }
95618
+ if (options.skipProjectBinaryInspection) {
95619
+ for (const record3 of reconstructed3.values()) {
95620
+ if (record3.recordKind === "project-file" && record3.fullData.status !== "uploaded") {
95621
+ invalidatedFileIds.add(record3.recordId);
95622
+ }
95623
+ }
95624
+ }
95491
95625
  for (const message of validateFinalizedFileAssignmentsV4(
95492
- [...reconstructed3.values()].map((record3) => ({
95626
+ Array.from(reconstructed3.values(), (record3) => ({
95493
95627
  recordKind: record3.recordKind,
95494
95628
  data: record3.fullData
95495
95629
  })),
@@ -95542,10 +95676,13 @@ function computeWorkspaceStatus(workspace, options) {
95542
95676
  intent: createNeoCliPushIntent(change),
95543
95677
  expectedBaseContentHash: change.kind === "create" ? null : change.casBaseHash ?? null
95544
95678
  })),
95545
- authoredValueSeeds: [...authoredValueSeeds].map(([memberId, seed]) => ({
95546
- memberId,
95547
- ...seed
95548
- }))
95679
+ authoredValueSeeds: Array.from(
95680
+ authoredValueSeeds,
95681
+ ([memberId, seed]) => ({
95682
+ memberId,
95683
+ ...seed
95684
+ })
95685
+ )
95549
95686
  });
95550
95687
  } catch (error) {
95551
95688
  parseErrors.push(
@@ -95868,12 +96005,23 @@ function prospectiveAnimationRecords(base, reconstructed3, changes, seeds) {
95868
96005
  const key = recordStateKey(change.recordKind, change.recordId);
95869
96006
  if (change.kind === "delete") prospective.delete(key);
95870
96007
  }
95871
- const projectRecord = [...prospective.values()].find(
95872
- (record3) => record3.recordKind === "project"
95873
- ) ?? [...reconstructed3.values()].filter((record3) => record3.recordKind === "project").map((record3) => ({
95874
- recordKind: record3.recordKind,
95875
- data: record3.fullData
95876
- }))[0];
96008
+ let projectRecord;
96009
+ for (const record3 of prospective.values()) {
96010
+ if (record3.recordKind === "project") {
96011
+ projectRecord = record3;
96012
+ break;
96013
+ }
96014
+ }
96015
+ if (projectRecord === void 0) {
96016
+ for (const record3 of reconstructed3.values()) {
96017
+ if (record3.recordKind !== "project") continue;
96018
+ projectRecord = {
96019
+ recordKind: record3.recordKind,
96020
+ data: record3.fullData
96021
+ };
96022
+ break;
96023
+ }
96024
+ }
95877
96025
  const projectId = projectRecord?.data.id;
95878
96026
  const seedEnvelope = typeof projectId === "string" ? { projectId, createdAt: 0, updatedAt: 0 } : {};
95879
96027
  for (const [key, record3] of reconstructed3) {
@@ -97274,43 +97422,47 @@ function compilePulledMemberInitializerBodyV4(document, memberId, compilationPro
97274
97422
  }
97275
97423
  function readPulledProjectDocumentV4(records2) {
97276
97424
  const raw = structuredClone(
97277
- pulledProjectDocumentRaw(replayWorkspace(records2))
97278
- );
97279
- return readProjectDocument(
97280
- completeProspectiveMemberCreateEnvelopes(
97281
- completeProspectiveLocalizedTexts(raw)
97282
- ),
97283
- {
97284
- constructors: "authored-or-compiled",
97285
- identities: "prospective",
97286
- members: "authored-or-compiled"
97287
- }
97425
+ pulledProjectDocumentRaw(
97426
+ replayWorkspace(completeProspectiveRecordCreateEnvelopes(records2))
97427
+ )
97288
97428
  );
97429
+ return readProjectDocument(completeProspectiveLocalizedTexts(raw), {
97430
+ constructors: "authored-or-compiled",
97431
+ identities: "prospective",
97432
+ members: "authored-or-compiled"
97433
+ });
97289
97434
  }
97290
- function completeProspectiveMemberCreateEnvelopes(raw) {
97291
- const members = raw.members;
97292
- if (!Array.isArray(members)) return raw;
97293
- if (!members.some(isPendingMemberWithoutCreateEnvelope)) return raw;
97294
- const project = raw.project;
97295
- if (!isObjectRecord2(project) || typeof project.id !== "string") {
97435
+ function completeProspectiveRecordCreateEnvelopes(records2) {
97436
+ let project;
97437
+ for (const record3 of records2.values()) {
97438
+ if (!record3.deleted && record3.recordKind === "project" && isObjectRecord2(record3.data)) {
97439
+ project = record3;
97440
+ break;
97441
+ }
97442
+ }
97443
+ const projectId = isObjectRecord2(project?.data) ? project.data.id : null;
97444
+ if (typeof projectId !== "string") {
97296
97445
  throw new Error(
97297
- "Pending source-authored members need the server create envelope, but the project record has no id."
97446
+ "Prospective source-authored records need the server create envelope, but the project record has no id."
97298
97447
  );
97299
97448
  }
97300
- return {
97301
- ...raw,
97302
- members: members.map(
97303
- (member) => isPendingMemberWithoutCreateEnvelope(member) ? {
97304
- projectId: project.id,
97449
+ const completed = /* @__PURE__ */ new Map();
97450
+ for (const [key, record3] of records2) {
97451
+ if (record3.deleted || record3.recordKind === "project" || !isObjectRecord2(record3.data) || typeof record3.data.id !== "string" || typeof record3.data.projectId === "string" && typeof record3.data.createdAt === "number" && typeof record3.data.updatedAt === "number") {
97452
+ completed.set(key, record3);
97453
+ continue;
97454
+ }
97455
+ completed.set(key, {
97456
+ ...record3,
97457
+ data: {
97458
+ projectId,
97305
97459
  createdAt: 0,
97306
97460
  updatedAt: 0,
97307
- ...member
97308
- } : member
97309
- )
97310
- };
97311
- }
97312
- function isPendingMemberWithoutCreateEnvelope(value) {
97313
- return isObjectRecord2(value) && typeof value.id === "string" && value.id.startsWith("__pending__:") && (typeof value.projectId !== "string" || typeof value.createdAt !== "number" || typeof value.updatedAt !== "number");
97461
+ ...record3.data
97462
+ }
97463
+ });
97464
+ }
97465
+ return completed;
97314
97466
  }
97315
97467
  function completeProspectiveLocalizedTexts(raw) {
97316
97468
  const localizedTexts = raw.localizedTexts;
@@ -109182,7 +109334,7 @@ function qualifiedMemberLabel(thisClass, member) {
109182
109334
  if (typeof thisClass.name !== "string") return `'${memberName}'`;
109183
109335
  return `'${thisClass.name}.${memberName}'`;
109184
109336
  }
109185
- function analyzeWorkspaceProjectManifestV4(workspace) {
109337
+ function analyzeWorkspaceProjectV4(workspace) {
109186
109338
  const status = computeWorkspaceStatus2(workspace, {
109187
109339
  skipProjectBinaryInspection: true
109188
109340
  });
@@ -109207,13 +109359,35 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
109207
109359
  data: record3.fullData
109208
109360
  });
109209
109361
  }
109210
- return documentsToProjectSchemaManifest(documents);
109362
+ const records2 = /* @__PURE__ */ new Map();
109363
+ for (const record3 of status.reconstructed.values()) {
109364
+ records2.set(`${record3.recordKind}:${record3.recordId}`, {
109365
+ recordKind: record3.recordKind,
109366
+ recordId: record3.recordId,
109367
+ contentHash: "local-script-check",
109368
+ deleted: false,
109369
+ data: record3.fullData
109370
+ });
109371
+ }
109372
+ return {
109373
+ manifest: documentsToProjectSchemaManifest(documents),
109374
+ document: readDocumentArrays(
109375
+ readPulledProjectDocumentV4(records2)
109376
+ )
109377
+ };
109211
109378
  }
109212
109379
  async function runScript(workspace, command, options, dependencies = {}) {
109213
109380
  let localCheckManifest = null;
109381
+ let localCheckDocument = null;
109214
109382
  const targetsLocalMember = (command === "check" || command === "compile") && options.memberRef !== null;
109215
109383
  if (command === "check" && options.all || targetsLocalMember) {
109216
- localCheckManifest = (dependencies.analyzeWorkspaceManifest ?? analyzeWorkspaceProjectManifestV4)(workspace);
109384
+ if (dependencies.analyzeWorkspaceManifest === void 0) {
109385
+ const localProject = analyzeWorkspaceProjectV4(workspace);
109386
+ localCheckManifest = localProject.manifest;
109387
+ localCheckDocument = localProject.document;
109388
+ } else {
109389
+ localCheckManifest = dependencies.analyzeWorkspaceManifest(workspace);
109390
+ }
109217
109391
  if (targetsLocalMember) {
109218
109392
  const localTarget = resolveLocalScriptMember(
109219
109393
  localCheckManifest,
@@ -109233,6 +109407,10 @@ async function runScript(workspace, command, options, dependencies = {}) {
109233
109407
  return;
109234
109408
  }
109235
109409
  }
109410
+ if (command === "check" && options.all && localCheckManifest !== null && localCheckDocument !== null) {
109411
+ runCheckAll(localCheckDocument, localCheckManifest, options);
109412
+ return;
109413
+ }
109236
109414
  const client = NeoApiClient.forBaseUrl(workspace.config.apiBaseUrl);
109237
109415
  const { raw } = await fetchProjectDocument(workspace);
109238
109416
  const document = readDocumentArrays(raw);
@@ -110176,6 +110354,7 @@ var init_script = __esm({
110176
110354
  init_workspace_status();
110177
110355
  init_source_diagnostics();
110178
110356
  init_project_documents();
110357
+ init_initializer_replay();
110179
110358
  init_push_body_diagnostics();
110180
110359
  RETURN_SHORTHANDS = {
110181
110360
  // MemberKind numerics: Bool=1, Int=2, String=3, Float=4.
@@ -113831,7 +114010,7 @@ var init_registry2 = __esm({
113831
114010
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
113832
114011
  formatVersion: 3,
113833
114012
  contractVersion: "3.14",
113834
- cliVersion: "0.36.1",
114013
+ cliVersion: "0.36.3",
113835
114014
  projectFileUploadBatchSize: 32,
113836
114015
  documentRecords: {
113837
114016
  member: {
@@ -120433,7 +120612,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
120433
120612
  async function main() {
120434
120613
  const args = parseArgs(process.argv.slice(2));
120435
120614
  if (args.command === "--version") {
120436
- console.log("0.36.1");
120615
+ console.log("0.36.3");
120437
120616
  return;
120438
120617
  }
120439
120618
  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.36.1",
3
+ "version": "0.36.3",
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.36.1 -->
12
+ <!-- reviewed-through-cli: 0.36.3 -->
13
13
 
14
14
  # Neo Compose CLI
15
15
 
@@ -83,7 +83,7 @@ wrappers.
83
83
  The marker near the top of `SKILL.md` must exactly match the package version:
84
84
 
85
85
  ```html
86
- <!-- reviewed-through-cli: 0.36.1 -->
86
+ <!-- reviewed-through-cli: 0.36.3 -->
87
87
  ```
88
88
 
89
89
  The quoted version above is checked too, so this instruction cannot go stale
@@ -154,6 +154,10 @@ components must be numeric literals. When a computed list or dictionary entry
154
154
  carries an authored `@id`, parenthesize the expression — `@id("row")
155
155
  (A.B / A.C)` — so the annotation names the row rather than its left operand.
156
156
 
157
+ Numeric literals do not use C# suffixes: write `0` or `0.5`, not `0f` or
158
+ `0.5f`. The declared/contextual type determines whether a literal is stored as
159
+ an int, float, or decimal.
160
+
157
161
  A direct field or property read is computed too, whether it is local,
158
162
  qualified static, or reached through a stable root path. For example,
159
163
  `protected int Slots = FeatureFlags.StartInventorySize;` evaluates the current
@@ -78,6 +78,11 @@ neo script eval --function Outpost.RefreshUnlock --this-value <id> --args '[3]'
78
78
 
79
79
  Prefer `--json` for automation.
80
80
 
81
+ `neo script check --all` reconstructs the prospective project entirely from
82
+ the local workspace. It checks local schema bodies and stored dialogue,
83
+ migration, and text-variable bodies against that same source transaction, so
84
+ new declarations do not depend on a prior push or a network fetch.
85
+
81
86
  Inside an inline Class body, unqualified names resolve against that Class;
82
87
  invoke delegate and action members directly with `Selector()` or `OnChanged()`.
83
88
  Spell a zero-argument action as bare `NeoAction`; type arguments name action
@@ -196,11 +201,13 @@ instead of hand-counting a literal or looping around `Add`:
196
201
  List<int> scores = List.Repeat(0, 10);
197
202
  ```
198
203
 
199
- The entry type is inferred from the value and joined with the assignment
200
- context, exactly like a list literal, so assigning `List.Repeat(0, 4)` to a
201
- `List<float>` widens the entries to `float`. Never spell type arguments on the
202
- qualifier: `List<int>.Repeat(0, 3)` is an error, and bare `List` is a
203
- qualifier, not a value.
204
+ The entry type is normally inferred from the value and joined with the
205
+ assignment context, exactly like a list literal, so assigning
206
+ `List.Repeat(0, 4)` to a `List<float>` widens the entries to `float`. When the
207
+ value cannot carry the type by itself, use a call-site type argument, for
208
+ example `List.Repeat<InventoryItemStack?>(null, slotCount)`. Never spell type
209
+ arguments on the qualifier: `List<int>.Repeat(0, 3)` is an error, and bare
210
+ `List` is a qualifier, not a value.
204
211
 
205
212
  `count` must be an `int` — a `float` count is a compile error, and a negative
206
213
  count fails at runtime with `List.Repeat count must be non-negative; got