@neocompose/cli 0.31.14 → 0.31.17

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,32 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.31.17] - 2026-08-18
4
+
5
+ ### Fixed
6
+
7
+ - Accept runtime interface patterns on open Class hierarchies while retaining
8
+ impossible-pattern diagnostics for sealed Classes.
9
+ - Recognize typed NeoScript setters as writable from initializers and function
10
+ bodies, including compound assignments, and compile their setter bodies.
11
+ - Preserve `NeoVariant<T>` member types in the local manifest used by
12
+ `neo script check --all`, matching `neo test` candidate compilation.
13
+
14
+ ## [0.31.16] - 2026-08-18
15
+
16
+ ### Fixed
17
+
18
+ - Resolve unordered List membership from entry `containerId` rows during
19
+ NeoScript evaluation, so root-backed indexes and collection operations see
20
+ stored entries in CLI tests, web previews, and world construction.
21
+
22
+ ## [0.31.15] - 2026-08-18
23
+
24
+ ### Fixed
25
+
26
+ - Accept authored internal record relation creates without server-owned project
27
+ and timestamp metadata, including durable pushes, and mirror the server's
28
+ metadata stamping during dry-run validation.
29
+
3
30
  ## [0.31.14] - 2026-08-18
4
31
 
5
32
  ### Fixed
package/dist/neo.mjs CHANGED
@@ -8817,6 +8817,18 @@ function typesOverlap(left, right, project) {
8817
8817
  if (isNull(right)) return isNullable(left);
8818
8818
  if (isNeoScriptTypeAssignable(left, right, project) || isNeoScriptTypeAssignable(right, left, project))
8819
8819
  return true;
8820
+ if (left.kind === "named" && right.kind === "named") {
8821
+ const leftDeclaration = project.typeById.get(left.typeId);
8822
+ const rightDeclaration = project.typeById.get(right.typeId);
8823
+ if (leftDeclaration?.kind === "interface" && rightDeclaration?.kind === "interface") {
8824
+ return true;
8825
+ }
8826
+ const classDeclaration = leftDeclaration?.kind === "class" ? leftDeclaration : rightDeclaration?.kind === "class" ? rightDeclaration : null;
8827
+ const interfaceDeclaration = leftDeclaration?.kind === "interface" ? leftDeclaration : rightDeclaration?.kind === "interface" ? rightDeclaration : null;
8828
+ if (classDeclaration !== null && interfaceDeclaration !== null) {
8829
+ return classDeclaration.sealed !== true;
8830
+ }
8831
+ }
8820
8832
  return isNumeric(left) && isNumeric(right);
8821
8833
  }
8822
8834
  function isUnknown(type) {
@@ -24110,6 +24122,7 @@ function buildProjectGraph(documents) {
24110
24122
  ...interfaceTypeIds.length ? { interfaceTypeIds } : {},
24111
24123
  ...typeParameters.length ? { typeParameters } : {},
24112
24124
  ...classDeclaration?.modifiers.includes("abstract") ? { abstract: true } : {},
24125
+ ...classDeclaration?.modifiers.includes("sealed") ? { sealed: true } : {},
24113
24126
  location: location(info.uri, info.declaration.range),
24114
24127
  selectionLocation: location(info.uri, info.declaration.nameRange)
24115
24128
  };
@@ -24612,10 +24625,23 @@ function accessorBodyRange(source, bodyRange, keyword) {
24612
24625
  continue;
24613
24626
  }
24614
24627
  if (depth !== 1 || token.text !== keyword) continue;
24615
- const open = tokens[index + 1];
24628
+ let openIndex = index + 1;
24629
+ if (tokens[openIndex]?.text === "(") {
24630
+ let parameterDepth = 1;
24631
+ for (let cursor = openIndex + 1; cursor < tokens.length; cursor++) {
24632
+ const candidate = tokens[cursor];
24633
+ if (candidate.text === "(") parameterDepth++;
24634
+ if (candidate.text === ")") parameterDepth--;
24635
+ if (parameterDepth !== 0) continue;
24636
+ openIndex = cursor + 1;
24637
+ break;
24638
+ }
24639
+ if (parameterDepth !== 0) return null;
24640
+ }
24641
+ const open = tokens[openIndex];
24616
24642
  if (open?.text !== "{") return null;
24617
24643
  let accessorDepth = 1;
24618
- for (let cursor = index + 2; cursor < tokens.length; cursor++) {
24644
+ for (let cursor = openIndex + 1; cursor < tokens.length; cursor++) {
24619
24645
  const candidate = tokens[cursor];
24620
24646
  if (candidate.text === "{") accessorDepth++;
24621
24647
  if (candidate.text === "}") accessorDepth--;
@@ -31325,6 +31351,7 @@ function schemaClass(value, environment, field) {
31325
31351
  `${field}.implementsInterfaceIds`
31326
31352
  ),
31327
31353
  ...value.declarationModifier === "abstract" ? { abstract: true } : {},
31354
+ ...value.isSealed === true ? { sealed: true } : {},
31328
31355
  ...sourceLocation(value.source, environment, `${field}.source`),
31329
31356
  typeParameters: records(
31330
31357
  value.genericParameters ?? [],
@@ -31945,6 +31972,20 @@ function memberType(member, environment, visiting, field) {
31945
31972
  nullable: false
31946
31973
  };
31947
31974
  }
31975
+ if (kind === "variant") {
31976
+ const variantClass = [...environment.classes.values()].find(
31977
+ (candidate) => candidate.name === NEO_VARIANT_TYPE_NAME
31978
+ );
31979
+ if (variantClass === void 0) return { ...unknownType(), nullable };
31980
+ return {
31981
+ kind: "named",
31982
+ typeId: string2(variantClass.id, `${field}.variantClass.id`),
31983
+ typeArguments: [
31984
+ manifestType2(member.targetType, environment, `${field}.targetType`)
31985
+ ],
31986
+ nullable
31987
+ };
31988
+ }
31948
31989
  if (kind === "generic") {
31949
31990
  return typeParameter(
31950
31991
  string2(member.genericParamId, `${field}.genericParamId`),
@@ -32487,6 +32528,7 @@ var init_project_schema_manifest = __esm({
32487
32528
  "use strict";
32488
32529
  init_project();
32489
32530
  init_project_schema_contract_generated();
32531
+ init_language_spec();
32490
32532
  }
32491
32533
  });
32492
32534
 
@@ -46337,6 +46379,23 @@ var init_member_kinds_db_response = __esm({
46337
46379
  });
46338
46380
 
46339
46381
  // ../src/models/members/unordered-list-membership.ts
46382
+ function buildUnorderedListMembershipIndex(values) {
46383
+ const membersByContainerId = /* @__PURE__ */ new Map();
46384
+ for (const candidate of values) {
46385
+ const containerId = candidate.containerId;
46386
+ if (typeof containerId !== "string") continue;
46387
+ const bucket = membersByContainerId.get(containerId);
46388
+ if (bucket === void 0) {
46389
+ membersByContainerId.set(containerId, [candidate.id]);
46390
+ } else {
46391
+ bucket.push(candidate.id);
46392
+ }
46393
+ }
46394
+ for (const bucket of membersByContainerId.values()) {
46395
+ bucket.sort();
46396
+ }
46397
+ return membersByContainerId;
46398
+ }
46340
46399
  function unorderedListEntryIds(values, listValue2, membershipIndex) {
46341
46400
  if (!Array.isArray(listValue2.value)) return [];
46342
46401
  if (membershipIndex !== void 0) {
@@ -49214,9 +49273,16 @@ function isProjectRecordKind(value) {
49214
49273
  return false;
49215
49274
  }
49216
49275
  function isInternalRecordRelation(value) {
49217
- if (!isWithId(value)) return false;
49276
+ if (!isAuthoredInternalRecordRelation(value)) return false;
49218
49277
  const relation = value;
49219
49278
  if (!isString(relation.projectId)) return false;
49279
+ if (!isNumber(relation.createdAt)) return false;
49280
+ return isNumber(relation.updatedAt);
49281
+ }
49282
+ function isAuthoredInternalRecordRelation(value) {
49283
+ if (!isWithId(value)) return false;
49284
+ if (value.id.length === 0) return false;
49285
+ const relation = value;
49220
49286
  if (!isString(relation.relationKind)) return false;
49221
49287
  if (relation.relationKind.length === 0) return false;
49222
49288
  if (!isProjectRecordKind(relation.sourceRecordKind)) return false;
@@ -49228,8 +49294,7 @@ function isInternalRecordRelation(value) {
49228
49294
  if (relation.orderKey !== void 0 && (!isString(relation.orderKey) || relation.orderKey.length === 0)) {
49229
49295
  return false;
49230
49296
  }
49231
- if (!isNumber(relation.createdAt)) return false;
49232
- return isNumber(relation.updatedAt);
49297
+ return true;
49233
49298
  }
49234
49299
  function isProjectVersionSemver(value) {
49235
49300
  if (!isObject(value)) return false;
@@ -63881,9 +63946,11 @@ function makeEvaluatorLookups(members, values, options = {}) {
63881
63946
  for (const a of members) attrMap.set(a.id, a);
63882
63947
  const valMap = /* @__PURE__ */ new Map();
63883
63948
  for (const v of values) valMap.set(v.id, v);
63949
+ const unorderedListMembership = buildUnorderedListMembershipIndex(values);
63884
63950
  const lookups = {
63885
63951
  memberById: (id2) => attrMap.get(id2) ?? null,
63886
- valueById: (id2) => valMap.get(id2) ?? null
63952
+ valueById: (id2) => valMap.get(id2) ?? null,
63953
+ unorderedListEntryIds: (containerValueId) => unorderedListMembership.get(containerValueId) ?? []
63887
63954
  };
63888
63955
  if (options.includeValueGraphIndexes === true) {
63889
63956
  return {
@@ -67830,9 +67897,24 @@ function resolveValueIfIdForMember(at, member, ctx) {
67830
67897
  );
67831
67898
  if (!row) return at;
67832
67899
  ctx.valueDependencies?.add(row.id);
67900
+ if (isMemberListBase(member) && member.listKind === "unordered" && Array.isArray(row.value)) {
67901
+ return evaluatorUnorderedListEntryIds(row.id, ctx);
67902
+ }
67833
67903
  const value = resolveLocalizedRowValueForMember(row, member, ctx);
67834
67904
  return shouldUnwrapSingleLookupValue(member) ? unwrapSingleLookupValue(value, ctx) : value;
67835
67905
  }
67906
+ function evaluatorUnorderedListEntryIds(containerValueId, ctx) {
67907
+ const runtimeRows = [...ctx.__runtimeSessionValues?.values() ?? []];
67908
+ const overlayRows = [...ctx.__valueOverlay?.values() ?? []];
67909
+ const localRows = [...runtimeRows, ...overlayRows];
67910
+ const shadowedIds = new Set(localRows.map((row) => row.id));
67911
+ const baseIds = ctx.vm.databaseVM?.unorderedListEntryIds?.(containerValueId) ?? ctx.vm.values.filter((row) => row.containerId === containerValueId).map((row) => row.id);
67912
+ const ids = new Set(baseIds.filter((id2) => !shadowedIds.has(id2)));
67913
+ for (const row of localRows) {
67914
+ if (row.containerId === containerValueId) ids.add(row.id);
67915
+ }
67916
+ return [...ids].sort();
67917
+ }
67836
67918
  function shouldUnwrapSingleLookupValue(member) {
67837
67919
  if (member === null) return false;
67838
67920
  if (!isMemberLookup(member)) return false;
@@ -71974,7 +72056,7 @@ function initializerEvaluatorLookups(document) {
71974
72056
  });
71975
72057
  return lookups;
71976
72058
  }
71977
- function buildInitializerRootValue(document, lookups) {
72059
+ function buildInitializerRootValueWithLookups(document, lookups) {
71978
72060
  const rootValue = {};
71979
72061
  for (const { root, memberId } of projectRootMembersInDisplayOrder(
71980
72062
  document.project
@@ -72014,7 +72096,7 @@ function evaluateMemberInitializer(args) {
72014
72096
  databaseVM
72015
72097
  },
72016
72098
  thisValue: null,
72017
- rootValue: buildInitializerRootValue(args.document, databaseVM),
72099
+ rootValue: buildInitializerRootValueWithLookups(args.document, databaseVM),
72018
72100
  saveStaticBindings: args.saveStaticBindings,
72019
72101
  sessionStaticBindings: args.sessionStaticBindings,
72020
72102
  ...args.storedConstructionReplay === true ? {
@@ -81254,23 +81336,6 @@ function isPushIntentInternalRecordRelation(value, operation) {
81254
81336
  }
81255
81337
  return isInternalRecordRelation(value);
81256
81338
  }
81257
- function isAuthoredInternalRecordRelation(value) {
81258
- if (!isObjectRecord5(value)) return false;
81259
- if (!isNonEmptyString3(value.id)) return false;
81260
- if (!isNonEmptyString3(value.relationKind)) return false;
81261
- if (!isProjectRecordKind(value.sourceRecordKind)) return false;
81262
- if (!isNonEmptyString3(value.sourceRecordId)) return false;
81263
- if (!isProjectRecordKind(value.targetRecordKind)) return false;
81264
- if (!isNonEmptyString3(value.targetRecordId)) return false;
81265
- if (value.orderKey === void 0) return true;
81266
- return isNonEmptyString3(value.orderKey);
81267
- }
81268
- function isObjectRecord5(value) {
81269
- return typeof value === "object" && value !== null && !Array.isArray(value);
81270
- }
81271
- function isNonEmptyString3(value) {
81272
- return typeof value === "string" && value.length > 0;
81273
- }
81274
81339
  var init_push_change_intent = __esm({
81275
81340
  "src/commands/push-change-intent.ts"() {
81276
81341
  "use strict";
@@ -94520,13 +94585,24 @@ function sortSeedRows(values) {
94520
94585
  return 0;
94521
94586
  });
94522
94587
  }
94523
- function stampServerOwnedSourceCommitTimestamps(args) {
94588
+ function stampServerOwnedSourceCommitMetadata(args) {
94524
94589
  if (!isObjectRecord2(args.rawDocument)) {
94525
94590
  throw new ProjectSourceCommitVerificationError(
94526
94591
  "Authoritative project document must be an object."
94527
94592
  );
94528
94593
  }
94529
94594
  const raw = args.rawDocument;
94595
+ const project = raw.project;
94596
+ if (!isObjectRecord2(project) || typeof project.id !== "string") {
94597
+ throw new ProjectSourceCommitVerificationError(
94598
+ "Authoritative project document must contain a project id."
94599
+ );
94600
+ }
94601
+ if (project.id.length === 0) {
94602
+ throw new ProjectSourceCommitVerificationError(
94603
+ "Authoritative project document project id must not be empty."
94604
+ );
94605
+ }
94530
94606
  const now = args.timestamp ?? Date.now();
94531
94607
  if (!Number.isSafeInteger(now)) {
94532
94608
  throw new ProjectSourceCommitVerificationError(
@@ -94542,6 +94618,11 @@ function stampServerOwnedSourceCommitTimestamps(args) {
94542
94618
  if (change.operation === "delete") return change;
94543
94619
  if (!isObjectRecord2(change.nextData)) return change;
94544
94620
  const stamped = { ...change.nextData };
94621
+ if (change.recordKind === "project") {
94622
+ delete stamped.projectId;
94623
+ } else {
94624
+ stamped.projectId = project.id;
94625
+ }
94545
94626
  if (change.operation === "create") {
94546
94627
  stamped.createdAt = now;
94547
94628
  stamped.updatedAt = now;
@@ -94698,7 +94779,7 @@ function prepareServerPreparationChanges(args) {
94698
94779
  `${change.recordKind}:${change.recordId}`
94699
94780
  )
94700
94781
  }));
94701
- const trustedChanges = stampServerOwnedSourceCommitTimestamps({
94782
+ const trustedChanges = stampServerOwnedSourceCommitMetadata({
94702
94783
  rawDocument,
94703
94784
  changes
94704
94785
  });
@@ -108671,11 +108752,11 @@ function assertProjectFileAuthoringRecordIdentifiers(recordKind, data) {
108671
108752
  if (recordKind !== "unity-texture-template" && recordKind !== "unity-audio-clip-template") {
108672
108753
  return;
108673
108754
  }
108674
- if (!isObjectRecord6(data) || typeof data.name !== "string") return;
108755
+ if (!isObjectRecord5(data) || typeof data.name !== "string") return;
108675
108756
  const label = recordKind === "unity-texture-template" ? "Unity texture template name" : "Unity audio clip template name";
108676
108757
  assertProjectFileAuthoringIdentifier(data.name, label);
108677
108758
  }
108678
- function isObjectRecord6(value) {
108759
+ function isObjectRecord5(value) {
108679
108760
  return typeof value === "object" && value !== null && !Array.isArray(value);
108680
108761
  }
108681
108762
  var init_project_file_authoring_identifiers = __esm({
@@ -108686,12 +108767,13 @@ var init_project_file_authoring_identifiers = __esm({
108686
108767
  });
108687
108768
 
108688
108769
  // ../src/database/project-version-validation.ts
108689
- function validateProjectRecordByKind(recordKind, data) {
108770
+ function validateProjectRecordByKind(recordKind, data, options = {}) {
108690
108771
  if (recordKind === ProjectRecordKind.Project) {
108691
108772
  if (isProject(data)) return;
108692
108773
  }
108693
108774
  if (recordKind === ProjectRecordKind.Member) {
108694
- if (isAnyMember(data)) return;
108775
+ const memberGuard = options.members === "authored-or-compiled" ? isAnyMemberAuthoredOrCompiled : isAnyMember;
108776
+ if (memberGuard(data)) return;
108695
108777
  }
108696
108778
  if (recordKind === ProjectRecordKind.Value) {
108697
108779
  if (isMemberValue(data)) return;
@@ -111624,7 +111706,7 @@ var init_registry2 = __esm({
111624
111706
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
111625
111707
  formatVersion: 3,
111626
111708
  contractVersion: "3.13",
111627
- cliVersion: "0.31.14",
111709
+ cliVersion: "0.31.17",
111628
111710
  projectFileUploadBatchSize: 32,
111629
111711
  documentRecords: {
111630
111712
  member: {
@@ -118219,7 +118301,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
118219
118301
  async function main() {
118220
118302
  const args = parseArgs(process.argv.slice(2));
118221
118303
  if (args.command === "--version") {
118222
- console.log("0.31.14");
118304
+ console.log("0.31.17");
118223
118305
  return;
118224
118306
  }
118225
118307
  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.31.14",
3
+ "version": "0.31.17",
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.31.14 -->
12
+ <!-- reviewed-through-cli: 0.31.17 -->
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.31.14 -->
86
+ <!-- reviewed-through-cli: 0.31.17 -->
87
87
  ```
88
88
 
89
89
  The quoted version above is checked too, so this instruction cannot go stale
@@ -56,6 +56,11 @@ or narrowing guard before the next access.
56
56
 
57
57
  Treat lookup-return values with the same nullable narrowing rules.
58
58
 
59
+ Runtime declaration patterns follow the possible runtime type, not only the
60
+ static declaration. An open Class value may match an otherwise unrelated
61
+ interface through a derived Class that implements it; a sealed Class with no
62
+ such relationship remains a compile-time impossible pattern.
63
+
59
64
  A declaration pattern introduced by `is` stays available after a guard when
60
65
  every path that falls through matched the pattern, including when the guard's
61
66
  other paths return from a nested block: